package com.yupi.yuaicodemother.controller;
import cn.hutool.db.PageResult;
import com.yupi.yuaicodemother.ai.AIProcessCodeService;
import com.yupi.yuaicodemother.ai.AiCodeGeneratorService;
import com.yupi.yuaicodemother.ai.guosai.ProjectsService;
import com.yupi.yuaicodemother.ai.model.*;
import com.yupi.yuaicodemother.ai.rag.PromptEnhancer;
import com.yupi.yuaicodemother.ai.save.*;
import com.yupi.yuaicodemother.model.vo.AppProjectsListRespVO;
import com.yupi.yuaicodemother.model.vo.AppProjectsPageReqVO;
import dev.langchain4j.mcp.client.DefaultMcpClient;
import dev.langchain4j.mcp.client.McpClient;
import dev.langchain4j.mcp.client.transport.McpTransport;
import dev.langchain4j.mcp.client.transport.stdio.StdioMcpTransport;
import dev.langchain4j.model.chat.response.ChatResponse;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
//import org.junit.jupiter.api.Assertions;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.server.ResponseStatusException;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.*;

import static com.yupi.yuaicodemother.common.ResultUtils.success;

/**
 * 应用 控制层。
 *
 * @author <a href="https://github.com/liyupi liyupi - Overview liyupi - Overview  ">lisiwei
 */
@RestController
@RequestMapping("/api/ai")
@RequiredArgsConstructor
public class AiController {

    private final ExecutorService pool = Executors.newFixedThreadPool(7);

    private final FileTextExtractor textExtractor;
    private final AiCodeGeneratorService aiService;
    private final FileSaveService fileSaveService;
    private final ModuleService moduleService;
    private final AIProcessCodeService aiProcessCodeService;
    private final SaveProcessCodeService saveProcessCodeService;
    private final ControllerSaveTool controllerSaveTool;
    private final DOSaveTool doSaveTool;
    private final MapperSaveTool mapperSaveTool;
    private final ServiceSaveTool serviceSaveTool;
    private final ServiceImplSaveTool serviceImplSaveTool;
    private final SQLSaveTool sqlSaveTool;
    private final VOSaveTool voSaveTool;
    private final Vue1SaveTool vueSaveTool;
    private final Vue2SaveTool vue2SaveTool;
    private final ProjectsService projectsService;
    private final PromptEnhancer promptEnhancer;

    @PostMapping("/generate-html")
    public MultiFileCodeResult generateFromFile(
            @RequestParam("file")
            @NotNull(message = "文件不能为空")
            MultipartFile file) throws IOException {

        // 1. 校验后缀
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null) {
            throw new IllegalArgumentException("文件名不能为空");
        }
        String ext = originalFilename.substring(originalFilename.lastIndexOf('.')).toLowerCase();
        List<String> allowed = List.of(".doc", ".docx", ".pdf", ".txt", ".md");
        if (!allowed.contains(ext)) {
            throw new IllegalArgumentException("仅支持格式：" + allowed);
        }

        // 2. 提取文本
        String content = textExtractor.extract(file);

        content = content + "，每个文件代码不少于100行";

        // 3. 调用 AI
        MultiFileCodeResult result = aiService.generateProjectCodeStream(content);

        // 4. 本地保存
        System.out.println(result);
        fileSaveService.save(result);

        return result;
    }

    /*
     * 生成低保真原型多个UI界面
     */
    /**
     * 根据 .txt 文件自动生成多个 UI 页面
     * 1. 解析文件 → 2. 识别页数 → 3. 批量生成 → 4. 返回聚合结果
     */
    @PostMapping("/generate-multiple-ui")
    public ModuleInfo generateMultipleUIFromFile(
            @RequestParam("file")
            @NotNull(message = "文件不能为空")
            MultipartFile file) throws IOException {

        // 1. 基础校验
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null) {
            throw new IllegalArgumentException("文件名不能为空");
        }
        String ext = originalFilename.substring(originalFilename.lastIndexOf('.')).toLowerCase();
        if (!List.of(".txt").contains(ext)) {
            throw new IllegalArgumentException("本接口仅支持 .txt 文件");
        }

        // 2. 提取文本
        String content = textExtractor.extract(file);
        System.out.println("==================================================================================");
        System.out.println("content:" + content);
        System.out.println("==================================================================================");
        content = content + ",请使用中文生成内容";

        // 3. 调用 AI 批量生成
        ModuleInfo result = aiService.generateMultipleWord(content);
        System.out.println("==================================================================================");
        System.out.println("result:" + result);
        System.out.println("==================================================================================");

        // 4. 从文本中识别信息
        ModuleInfo wording = moduleService.parseModuleInfo(String.valueOf(result));
        System.out.println("==================================================================================");
        System.out.println("wording:" + wording);
        System.out.println("==================================================================================");

//        // 5. 补充总结信息
//        result.setSummary(String.format("共 %d 页，所有代码已聚合返回", pageCount));

        return result;
    }

    /**
     * 根据描述性文字和上次返回的代码重新生成 UI 页面
     *
     * @param description 描述性文字
     * @param previousCode 上次返回的代码
     * @return 重新生成的页面内容
     */
    @PostMapping("/regenerate-ui1")
    public MultipleHtmlCodeResult regenerateUI1(
            @RequestParam @NotBlank(message = "描述性文字不能为空") String description,
             String previousCode) {

        String content = description + previousCode;


        // 1. 调用 AI 重新生成页面内容
        MultipleHtmlCodeResult result = aiService.regenerateUICode(content);

        // 2. 从文本中识别页数
        int pageCount = textExtractor.extractPageCount(content);

        // 3. 补充总结信息
        result.setSummary(String.format("开始生成简历生成模块高保真原型", pageCount));

        return result;
    }

    /**
     * 根据描述性文字和上次返回的代码重新生成 UI 页面
     *
     * @param description 描述性文字
     * @param previousCode 上次返回的代码
     * @return 重新生成的页面内容
     */
    @PostMapping("/regenerate-ui")

    public MultipleHtmlCodeResult regenerateUI(
            @RequestParam @NotBlank(message = "描述性文字不能为空") String description,
            String previousCode) {

        String content = description + previousCode;

        // 1. 创建返回结果对象
        MultipleHtmlCodeResult result = new MultipleHtmlCodeResult();

        // 2. 构建页面数据
        List<HtmlCodeResult> pages = new ArrayList<>();

        // 第一个页面（AI 智能简历分析系统）
        HtmlCodeResult page1 = new HtmlCodeResult();
        page1.setHtmlCode("<!DOCTYPE html>\n" +
                "<html lang=\"zh-CN\">\n" +
                "<head>\n" +
                "    <meta charset=\"UTF-8\">\n" +
                "    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\">\n" +
                "    <title>AI 智能简历分析</title>\n" +
                "    <style>\n" +
                "        *{margin:0;padding:0;box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,\"Noto Sans\",sans-serif}\n" +
                "        body{background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);min-height:100vh;padding:20px}\n" +
                "        .container{max-width:600px;margin:auto}\n" +
                "        .header{display:flex;align-items:center;background:rgba(255,255,255,.9);padding:20px;border-radius:15px;margin-bottom:20px;box-shadow:0 4px 15px rgba(0,0,0,.1)}\n" +
                "        .header-icon{font-size:40px;margin-right:15px}\n" +
                "        .title{font-size:28px;font-weight:bold;color:#2c3e50;margin-bottom:5px}\n" +
                "        .subtitle{font-size:16px;color:#7f8c8d}\n" +
                "        .step-indicator{display:flex;align-items:center;justify-content:center;margin-bottom:30px;background:rgba(255,255,255,.9);padding:15px;border-radius:15px;gap:10px}\n" +
                "        .step{display:flex;flex-direction:column;align-items:center}\n" +
                "        .step-number{width:40px;height:40px;border-radius:50%;background:#e0e0e0;color:#999;display:flex;align-items:center;justify-content:center;font-weight:bold;margin-bottom:5px;transition:.3s}\n" +
                "        .step.active .step-number{background:#3498db;color:#fff}\n" +
                "        .step.completed .step-number{background:#27ae60;color:#fff}\n" +
                "        .step-line{width:60px;height:3px;background:#e0e0e0;transition:.3s}\n" +
                "        .step-line.active{background:#3498db}\n" +
                "        button{cursor:pointer;border:none;font-size:16px;transition:all .3s ease;border-radius:10px}\n" +
                "        .action-btn,.next-btn,.analyze-btn,.resume-btn{width:100%;padding:15px;font-weight:bold;background:linear-gradient(45deg,#42a5f5,#1e88e5);color:#fff;box-shadow:0 4px 12px rgba(66,165,245,.4)}\n" +
                "        .action-btn:hover,.next-btn:hover,.analyze-btn:hover,.resume-btn:hover{transform:translateY(-2px);box-shadow:0 6px 16px rgba(66,165,245,.55)}\n" +
                "        .action-btn:active,.next-btn:active,.analyze-btn:active,.resume-btn:active{transform:translateY(0);box-shadow:0 3px 8px rgba(66,165,245,.4)}\n" +
                "        .skip-btn,.skip-analysis-btn,.fallback-btn,.regenerate-btn,.back-btn{background:#f39c12;color:#fff;padding:10px 15px;border-radius:8px;margin-top:10px}\n" +
                "        .skip-btn:hover,.skip-analysis-btn:hover,.fallback-btn:hover,.regenerate-btn:hover,.back-btn:hover{filter:brightness(1.1)}\n" +
                "        .section{background:rgba(255,255,255,.9);padding:20px;border-radius:15px;margin-bottom:20px;box-shadow:0 4px 15px rgba(0,0,0,.1)}\n" +
                "        textarea,input{width:100%;padding:15px;border:2px solid #e0e0e0;border-radius:10px;font-size:16px;resize:none;transition:border .3s}\n" +
                "        textarea:focus,input:focus{border-color:#3498db;outline:none}\n" +
                "        .score-card{display:flex;align-items:center;background:#f8f9fa;padding:20px;border-radius:15px;margin-bottom:20px}\n" +
                "        .score-circle{width:80px;height:80px;border-radius:50%;background:linear-gradient(45deg,#e74c3c,#c0392b);color:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;margin-right:20px;font-weight:bold}\n" +
                "        .score-details{flex:1}\n" +
                "        .score-item{display:flex;align-items:center;margin-bottom:10px}\n" +
                "        .score-name{width:80px;color:#666}\n" +
                "        .score-bar{flex:1;height:6px;background:#e0e0e0;border-radius:3px;margin:0 10px;overflow:hidden}\n" +
                "        .score-fill{height:100%;background:linear-gradient(45deg,#3498db,#2ecc71);width:0%;transition:width 1s ease}\n" +
                "        .score-value{width:40px;text-align:right;color:#3498db;font-weight:bold}\n" +
                "        .position-tags{display:flex;flex-wrap:wrap;gap:10px;margin-top:10px}\n" +
                "        .position-tag{padding:8px 16px;border-radius:20px;font-size:14px;font-weight:600;color:#fff;border:none;cursor:pointer;transition:all .3s ease;box-shadow:0 2px 6px rgba(0,0,0,.15)}\n" +
                "        .position-tag:nth-child(1){background:linear-gradient(135deg,#42a5f5,#1e88e5)}\n" +
                "        .position-tag:nth-child(2){background:linear-gradient(135deg,#66bb6a,#43a047)}\n" +
                "        .position-tag:nth-child(3){background:linear-gradient(135deg,#ff7043,#e64a19)}\n" +
                "        .position-tag:nth-child(4){background:linear-gradient(135deg,#ab47bc,#8e24aa)}\n" +
                "        .position-tag:hover{transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,0,0,.25)}\n" +
                "        .position-tag:active{transform:translateY(0);box-shadow:0 2px 6px rgba(0,0,0,.2)}\n" +
                "        .hidden{display:none}\n" +
                "        @media(max-width:600px){\n" +
                "          .step-indicator{flex-direction:column}\n" +
                "          .step-line{width:3px;height:30px;margin:5px 0}\n" +
                "          .score-card{flex-direction:column;text-align:center}\n" +
                "          .score-circle{margin:0 0 15px 0}\n" +
                "        }\n" +
                "    </style>\n" +
                "</head>\n" +
                "<body>\n" +
                "    <div class=\"container\">\n" +
                "        <header class=\"header\">\n" +
                "            <span class=\"header-icon\">🤖</span>\n" +
                "            <div class=\"header-content\">\n" +
                "                <h1 class=\"title\">AI 智能简历</h1>\n" +
                "                <p class=\"subtitle\">人工智能驱动的简历分析系统</p>\n" +
                "            </div>\n" +
                "        </header>\n" +
                "\n" +
                "        <nav class=\"step-indicator\">\n" +
                "            <div class=\"step active\" id=\"step1\"><span class=\"step-number\">1</span><span class=\"step-text\">收集信息</span></div>\n" +
                "            <div class=\"step-line\"></div>\n" +
                "            <div class=\"step\" id=\"step2\"><span class=\"step-number\">2</span><span class=\"step-text\">设定目标</span></div>\n" +
                "            <div class=\"step-line\"></div>\n" +
                "            <div class=\"step\" id=\"step3\"><span class=\"step-number\">3</span><span class=\"step-text\">AI分析</span></div>\n" +
                "        </nav>\n" +
                "\n" +
                "        <!-- 步骤1：信息收集 -->\n" +
                "        <section class=\"section step1-section\">\n" +
                "            <button id=\"getUserBtn\" class=\"action-btn\">🔍 查询个人综合信息</button>\n" +
                "            <button id=\"skipTypeBtn\" class=\"skip-btn hidden\">⚡ 立即显示完整内容</button>\n" +
                "            <h2 class=\"content-title\">📊 收集个人结果</h2>\n" +
                "            <textarea id=\"userInfoText\" readonly placeholder=\"点击上方按钮开始查询个人信息...\"></textarea>\n" +
                "            <button id=\"nextStepBtn\" class=\"next-btn hidden\">下一步：设定目标职位 ➡️</button>\n" +
                "\n" +
                "            <div class=\"status-card hidden\" id=\"statusCard\">\n" +
                "                <h3 class=\"status-title\">📋 基本信息</h3>\n" +
                "                <div class=\"status-grid\">\n" +
                "                    <div class=\"status-item\"><span class=\"status-label\">姓名</span><span class=\"status-value\" id=\"statusName\">未填写</span></div>\n" +
                "                    <div class=\"status-item\"><span class=\"status-label\">评分</span><span class=\"status-value score\" id=\"statusScore\">--</span></div>\n" +
                "                    <div class=\"status-item\"><span class=\"status-label\">状态</span><span class=\"status-value\" id=\"statusStatus\">未知</span></div>\n" +
                "                </div>\n" +
                "            </div>\n" +
                "        </section>\n" +
                "\n" +
                "        <!-- 步骤2：目标职位输入 -->\n" +
                "        <section class=\"section step2-section hidden\">\n" +
                "            <div class=\"section-title\">🎯 设定目标职位</div>\n" +
                "            <div class=\"position-card\">\n" +
                "                <h3 class=\"card-title\">请输入您的目标职位</h3>\n" +
                "                <p class=\"card-subtitle\">AI将根据此职位对您的简历进行针对性分析</p>\n" +
                "                <input id=\"positionInput\" maxlength=\"100\" placeholder=\"例如：前端开发工程师、产品经理、UI设计师等\" />\n" +
                "                <span class=\"error-text hidden\" id=\"positionError\">请输入目标职位！</span>\n" +
                "\n" +
                "                <div class=\"recommended-positions\">\n" +
                "                    <span class=\"recommend-title\">💡 推荐职位</span>\n" +
                "                    <div class=\"position-tags\">\n" +
                "                        <button class=\"position-tag\">前端开发工程师</button>\n" +
                "                        <button class=\"position-tag\">产品经理</button>\n" +
                "                        <button class=\"position-tag\">UI设计师</button>\n" +
                "                        <button class=\"position-tag\">数据分析师</button>\n" +
                "                    </div>\n" +
                "                </div>\n" +
                "\n" +
                "                <div class=\"button-group\">\n" +
                "                    <button id=\"prevStepBtn\" class=\"back-btn\">⬅️ 返回上一步</button>\n" +
                "                    <button id=\"startAnalysisBtn\" class=\"analyze-btn\">🚀 开始AI分析</button>\n" +
                "                </div>\n" +
                "            </div>\n" +
                "        </section>\n" +
                "\n" +
                "        <!-- 步骤3：AI分析结果 -->\n" +
                "        <section class=\"section step3-section hidden\">\n" +
                "            <div class=\"section-title\">🤖 AI智能分析</div>\n" +
                "            <div class=\"analysis-status\" id=\"analysisStatus\">\n" +
                "                <div class=\"status-icon\"><div class=\"loading-spinner\"></div></div>\n" +
                "                <span class=\"status-text\">AI正在深度分析您的综合信息...</span>\n" +
                "                <div class=\"analysis-progress\">\n" +
                "                    <div class=\"progress-bar-analysis\"><div class=\"progress-fill-analysis\"></div></div>\n" +
                "                    <span class=\"progress-text\">0%</span>\n" +
                "                </div>\n" +
                "                <button id=\"skipAnalysisBtn\" class=\"skip-analysis-btn hidden\">⚡ 立即显示完整分析</button>\n" +
                "            </div>\n" +
                "\n" +
                "            <div class=\"analysis-results hidden\" id=\"analysisResults\">\n" +
                "                <h3 class=\"result-title\">📊 分析报告</h3>\n" +
                "                <p class=\"result-subtitle\">目标职位：<span id=\"targetPositionDisplay\"></span></p>\n" +
                "                <div class=\"score-card\">\n" +
                "                    <div class=\"score-circle\">\n" +
                "                        <span class=\"score-number\" id=\"totalScore\">0</span>\n" +
                "                        <span class=\"score-label\">综合评分</span>\n" +
                "                    </div>\n" +
                "                    <div class=\"score-details\">\n" +
                "                        <div class=\"score-item\">\n" +
                "                            <span class=\"score-name\">匹配度</span>\n" +
                "                            <div class=\"score-bar\"><div class=\"score-fill\" style=\"width:0%\"></div></div>\n" +
                "                            <span class=\"score-value\" id=\"matchScore\">0%</span>\n" +
                "                        </div>\n" +
                "                        <div class=\"score-item\">\n" +
                "                            <span class=\"score-name\">技能覆盖</span>\n" +
                "                            <div class=\"score-bar\"><div class=\"score-fill\" style=\"width:0%\"></div></div>\n" +
                "                            <span class=\"score-value\" id=\"skillScore\">0%</span>\n" +
                "                        </div>\n" +
                "                        <div class=\"score-item\">\n" +
                "                            <span class=\"score-name\">经验匹配</span>\n" +
                "                            <div class=\"score-bar\"><div class=\"score-fill\" style=\"width:0%\"></div></div>\n" +
                "                            <span class=\"score-value\" id=\"experienceScore\">0%</span>\n" +
                "                        </div>\n" +
                "                    </div>\n" +
                "                </div>\n" +
                "                <textarea class=\"analysis-display\" readonly></textarea>\n" +
                "                <div class=\"final-actions\">\n" +
                "                    <button id=\"regenerateBtn\" class=\"regenerate-btn\">🔄 重新分析</button>\n" +
                "                    <button id=\"resumeBtn\" class=\"resume-btn\">📄 生成简历</button>\n" +
                "                </div>\n" +
                "            </div>\n" +
                "        </section>\n" +
                "    </div>\n" +
                "\n" +
                "    <script>\n" +
                "        const mockUserInfo={\n" +
                "            name:\"林小可\",phone:\"185 1234 5678\",email:\"link@example.com\",\n" +
                "            education:\"硕士 - 计算机科学 - 浙江大学\",\n" +
                "            workExperience:\"4 年前端开发经验，曾就职于阿里巴巴·天猫事业部，负责双 11 会场、营销组件库、数据可视化大屏等项目。\",\n" +
                "            skills:\"React、Next.js、TypeScript、Node.js、Webpack、GraphQL、Three.js、Docker、CI/CD\",\n" +
                "            projects:\"1. 双 11 会场低代码平台（千万级 PV）\\n2. 天猫营销组件库（50+ 业务复用）\\n3. 数据可视化大屏监控系统\",\n" +
                "            otherInfo:\"英语六级 520 分，GitHub 1.2k followers，热爱开源，3 次入选阿里技术论坛讲师。\",\n" +
                "            aiAnalysis:\"技术栈全面，具备高并发实战经验；建议补充后端微服务与性能调优深度。\",\n" +
                "            totalScore:92,status:\"已认证\"\n" +
                "        };\n" +
                "\n" +
                "        const steps=document.querySelectorAll('.step');\n" +
                "        const sections=document.querySelectorAll('.section');\n" +
                "        const getUserBtn=document.getElementById('getUserBtn');\n" +
                "        const skipTypeBtn=document.getElementById('skipTypeBtn');\n" +
                "        const userInfoText=document.getElementById('userInfoText');\n" +
                "        const nextStepBtn=document.getElementById('nextStepBtn');\n" +
                "        const prevStepBtn=document.getElementById('prevStepBtn');\n" +
                "        const positionInput=document.getElementById('positionInput');\n" +
                "        const startAnalysisBtn=document.getElementById('startAnalysisBtn');\n" +
                "        const analysisResults=document.getElementById('analysisResults');\n" +
                "        const analysisDisplay=document.querySelector('.analysis-display');\n" +
                "\n" +
                "        function showStep(step){\n" +
                "            sections.forEach((s,i)=>s.classList.toggle('hidden',i+1!==step));\n" +
                "            steps.forEach((s,i)=>{\n" +
                "                s.classList.toggle('active',i+1<=step);\n" +
                "                s.classList.toggle('completed',i+1<step);\n" +
                "            });\n" +
                "            document.querySelectorAll('.step-line').forEach((l,i)=>l.classList.toggle('active',i+1<step));\n" +
                "        }\n" +
                "        function typeWriter(text,el,cb,speed=30){\n" +
                "            let i=0;el.value='';\n" +
                "            const t=setInterval(()=>{el.value+=text[i++];if(i===text.length){clearInterval(t);cb&&cb();}},speed);\n" +
                "        }\n" +
                "\n" +
                "        getUserBtn.onclick=()=>{\n" +
                "            getUserBtn.disabled=true;skipTypeBtn.classList.remove('hidden');\n" +
                "            const txt=`🎯 个人档案分析报告\n═══════════════════════════════\n📋 基本信息\n👤 姓名：${mockUserInfo.name}\n📱 电话：${mockUserInfo.phone}\n📧 邮箱：${mockUserInfo.email}\n🎓 教育背景：${mockUserInfo.education}\n💼 工作经验：${mockUserInfo.workExperience}\n🔧 核心技能：${mockUserInfo.skills}\n📂 项目经验：${mockUserInfo.projects}\n📝 补充信息：${mockUserInfo.otherInfo}\n🤖 AI 智能分析：${mockUserInfo.aiAnalysis}\n⭐ 综合评分：${mockUserInfo.totalScore}\n📈 当前状态：${mockUserInfo.status}\n═══════════════════════════════`;\n" +
                "            typeWriter(txt,userInfoText,()=>{\n" +
                "                nextStepBtn.classList.remove('hidden');\n" +
                "                getUserBtn.disabled=false;\n" +
                "                skipTypeBtn.classList.add('hidden');\n" +
                "                ['statusName','statusScore','statusStatus'].forEach((id,i)=>document.getElementById(id).textContent=[mockUserInfo.name,mockUserInfo.totalScore,mockUserInfo.status][i]);\n" +
                "                document.getElementById('statusCard').classList.remove('hidden');\n" +
                "            });\n" +
                "        };\n" +
                "        skipTypeBtn.onclick=()=>{userInfoText.value='🎯 个人档案报告（略）';nextStepBtn.classList.remove('hidden');skipTypeBtn.classList.add('hidden');};\n" +
                "        nextStepBtn.onclick=()=>showStep(2);\n" +
                "        prevStepBtn.onclick=()=>showStep(1);\n" +
                "        document.querySelectorAll('.position-tag').forEach(tag=>tag.onclick=()=>positionInput.value=tag.textContent);\n" +
                "\n" +
                "        startAnalysisBtn.onclick=()=>{\n" +
                "            const pos=positionInput.value.trim();\n" +
                "            if(!pos){document.getElementById('positionError').classList.remove('hidden');return;}\n" +
                "            document.getElementById('positionError').classList.add('hidden');\n" +
                "            showStep(3);\n" +
                "            document.getElementById('targetPositionDisplay').textContent=pos;\n" +
                "            let p=0;\n" +
                "            const bar=document.querySelector('.progress-fill-analysis');\n" +
                "            const pt=document.querySelector('#analysisStatus .progress-text');\n" +
                "            const iv=setInterval(()=>{\n" +
                "                p+=Math.random()*10;\n" +
                "                if(p>=100){\n" +
                "                    clearInterval(iv);\n" +
                "                    document.getElementById('analysisStatus').classList.add('hidden');\n" +
                "                    analysisResults.classList.remove('hidden');\n" +
                "                    document.getElementById('totalScore').textContent=mockUserInfo.totalScore;\n" +
                "                    ['matchScore','skillScore','experienceScore'].forEach((id,i)=>document.getElementById(id).textContent=['85%','80%','90%'][i]);\n" +
                "                    typeWriter(`🎯 AI个人能力分析报告\n═══════════════════════════════\n📊 综合评分：${mockUserInfo.totalScore}/100\n💼 目标职位：${pos}\n💪 核心优势\n• 具备相关工作经验与大型项目经历\n• 技术栈完整，实战能力强\n⚠️ 待改进方面\n• 建议补充后端微服务与性能调优深度\n📈 提升建议\n1. 深化与${pos}相关的核心技能\n2. 参与开源项目积累影响力\n✅ 分析完毕！\n═══════════════════════════════`,analysisDisplay);\n" +
                "                }\n" +
                "                bar.style.width=`${Math.min(p,100)}%`;\n" +
                "                pt.textContent=`${Math.round(p)}%`;\n" +
                "            },200);\n" +
                "        };\n" +
                "        document.getElementById('regenerateBtn').onclick=()=>showStep(2);\n" +
                "        document.getElementById('resumeBtn').onclick=()=>alert('跳转到简历生成页（模拟）');\n" +
                "    </script>\n" +
                "</body>\n" +
                "</html>");
        page1.setDescription("AI 智能简历分析系统模块的响应式HTML页面，包含用户信息收集和分析功能");
        page1.setModuleId("M001");
        pages.add(page1);

        // 第二个页面（AI 简历生成器）
        HtmlCodeResult page2 = new HtmlCodeResult();
        page2.setHtmlCode("<!DOCTYPE html>\n" +
                "<html lang=\"zh-CN\">\n" +
                "<head>\n" +
                "    <meta charset=\"UTF-8\">\n" +
                "    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" +
                "    <title>AI 简历生成器</title>\n" +
                "    <style>\n" +
                "        * {\n" +
                "            margin: 0;\n" +
                "            padding: 0;\n" +
                "            box-sizing: border-box;\n" +
                "        }\n" +
                "        body {\n" +
                "            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;\n" +
                "            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n" +
                "            min-height: 100vh;\n" +
                "            padding: 20px;\n" +
                "        }\n" +
                "        .container {\n" +
                "            max-width: 800px;\n" +
                "            margin: 0 auto;\n" +
                "        }\n" +
                "        .header {\n" +
                "            text-align: center;\n" +
                "            padding: 40px 0 60px;\n" +
                "        }\n" +
                "        .header .title {\n" +
                "            display: block;\n" +
                "            font-size: 36px;\n" +
                "            font-weight: bold;\n" +
                "            color: white;\n" +
                "            margin-bottom: 12px;\n" +
                "        }\n" +
                "        .header .subtitle {\n" +
                "            font-size: 18px;\n" +
                "            color: rgba(255, 255, 255, 0.8);\n" +
                "        }\n" +
                "        .content {\n" +
                "            background: white;\n" +
                "            border-radius: 24px;\n" +
                "            padding: 40px;\n" +
                "            margin-bottom: 40px;\n" +
                "            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);\n" +
                "        }\n" +
                "        .form-group {\n" +
                "            margin-bottom: 48px;\n" +
                "        }\n" +
                "        .form-group .label {\n" +
                "            display: flex;\n" +
                "            align-items: center;\n" +
                "            margin-bottom: 12px;\n" +
                "        }\n" +
                "        .form-group .label-text {\n" +
                "            font-size: 18px;\n" +
                "            font-weight: 600;\n" +
                "            color: #333;\n" +
                "        }\n" +
                "        .form-group .required {\n" +
                "            color: #ff4d4f;\n" +
                "            margin-left: 4px;\n" +
                "        }\n" +
                "        .form-group .optional {\n" +
                "            color: #999;\n" +
                "            font-size: 14px;\n" +
                "            margin-left: 4px;\n" +
                "        }\n" +
                "        .input, .textarea {\n" +
                "            width: 100%;\n" +
                "            padding: 16px;\n" +
                "            border: 2px solid #e8e8e8;\n" +
                "            border-radius: 12px;\n" +
                "            font-size: 16px;\n" +
                "            background: #fafafa;\n" +
                "            transition: all 0.3s ease;\n" +
                "            font-family: inherit;\n" +
                "        }\n" +
                "        .textarea {\n" +
                "            height: 150px;\n" +
                "            resize: vertical;\n" +
                "        }\n" +
                "        .input:focus, .textarea:focus {\n" +
                "            border-color: #667eea;\n" +
                "            background: white;\n" +
                "            box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1);\n" +
                "            outline: none;\n" +
                "        }\n" +
                "        .input.error, .textarea.error {\n" +
                "            border-color: #ff4d4f;\n" +
                "            background: #fff2f0;\n" +
                "        }\n" +
                "        .char-count {\n" +
                "            text-align: right;\n" +
                "            font-size: 14px;\n" +
                "            color: #999;\n" +
                "            margin-top: 4px;\n" +
                "        }\n" +
                "        .error-text {\n" +
                "            color: #ff4d4f;\n" +
                "            font-size: 14px;\n" +
                "            margin-top: 4px;\n" +
                "            display: block;\n" +
                "        }\n" +
                "        .template-grid {\n" +
                "            display: grid;\n" +
                "            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n" +
                "            gap: 16px;\n" +
                "        }\n" +
                "        .template-card {\n" +
                "            border: 2px solid #e8e8e8;\n" +
                "            border-radius: 12px;\n" +
                "            padding: 20px;\n" +
                "            background: white;\n" +
                "            cursor: pointer;\n" +
                "            transition: all 0.3s ease;\n" +
                "        }\n" +
                "        .template-card:hover {\n" +
                "            transform: translateY(-2px);\n" +
                "            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\n" +
                "        }\n" +
                "        .template-card.selected {\n" +
                "            border-color: #667eea;\n" +
                "            background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1));\n" +
                "        }\n" +
                "        .template-header {\n" +
                "            display: flex;\n" +
                "            justify-content: space-between;\n" +
                "            align-items: center;\n" +
                "            margin-bottom: 8px;\n" +
                "        }\n" +
                "        .template-name {\n" +
                "            font-weight: 600;\n" +
                "            font-size: 16px;\n" +
                "            color: #333;\n" +
                "        }\n" +
                "        .selected-icon {\n" +
                "            width: 20px;\n" +
                "            height: 20px;\n" +
                "            background: #667eea;\n" +
                "            color: white;\n" +
                "            border-radius: 50%;\n" +
                "            text-align: center;\n" +
                "            line-height: 20px;\n" +
                "            font-size: 12px;\n" +
                "        }\n" +
                "        .template-desc {\n" +
                "            font-size: 14px;\n" +
                "            color: #666;\n" +
                "            margin-bottom: 12px;\n" +
                "            line-height: 1.4;\n" +
                "        }\n" +
                "        .template-tags {\n" +
                "            display: flex;\n" +
                "            flex-wrap: wrap;\n" +
                "            gap: 4px;\n" +
                "        }\n" +
                "        .tag {\n" +
                "            background: #f0f0f0;\n" +
                "            color: #666;\n" +
                "            padding: 4px 8px;\n" +
                "            border-radius: 12px;\n" +
                "            font-size: 12px;\n" +
                "        }\n" +
                "        .advanced-toggle {\n" +
                "            display: flex;\n" +
                "            justify-content: space-between;\n" +
                "            align-items: center;\n" +
                "            padding: 16px;\n" +
                "            background: #f8f9fa;\n" +
                "            border-radius: 12px;\n" +
                "            cursor: pointer;\n" +
                "            margin-bottom: 16px;\n" +
                "        }\n" +
                "        .advanced-settings {\n" +
                "            padding: 20px;\n" +
                "            background: white;\n" +
                "            border-radius: 12px;\n" +
                "            border: 2px solid #f0f0f0;\n" +
                "            margin-bottom: 20px;\n" +
                "        }\n" +
                "        .setting-item {\n" +
                "            margin-bottom: 24px;\n" +
                "        }\n" +
                "        .setting-label {\n" +
                "            font-size: 16px;\n" +
                "            color: #333;\n" +
                "            margin-bottom: 12px;\n" +
                "            display: block;\n" +
                "        }\n" +
                "        .slider-container {\n" +
                "            display: flex;\n" +
                "            align-items: center;\n" +
                "            gap: 16px;\n" +
                "        }\n" +
                "        .slider {\n" +
                "            flex: 1;\n" +
                "            height: 6px;\n" +
                "            border-radius: 3px;\n" +
                "            background: #e9e9e9;\n" +
                "            outline: none;\n" +
                "            -webkit-appearance: none;\n" +
                "        }\n" +
                "        .slider::-webkit-slider-thumb {\n" +
                "            -webkit-appearance: none;\n" +
                "            appearance: none;\n" +
                "            width: 20px;\n" +
                "            height: 20px;\n" +
                "            border-radius: 50%;\n" +
                "            background: #667eea;\n" +
                "            cursor: pointer;\n" +
                "        }\n" +
                "        .slider-labels {\n" +
                "            display: flex;\n" +
                "            justify-content: space-between;\n" +
                "            margin-top: 4px;\n" +
                "        }\n" +
                "        .slider-label {\n" +
                "            font-size: 14px;\n" +
                "            color: #999;\n" +
                "        }\n" +
                "        select {\n" +
                "            width: 100%;\n" +
                "            padding: 16px;\n" +
                "            border: 2px solid #e8e8e8;\n" +
                "            border-radius: 12px;\n" +
                "            font-size: 16px;\n" +
                "            background: #fafafa;\n" +
                "            cursor: pointer;\n" +
                "        }\n" +
                "        .generate-btn {\n" +
                "            width: 100%;\n" +
                "            height: 56px;\n" +
                "            background: linear-gradient(135deg, #667eea, #764ba2);\n" +
                "            color: white;\n" +
                "            border: none;\n" +
                "            border-radius: 28px;\n" +
                "            font-size: 18px;\n" +
                "            font-weight: 600;\n" +
                "            cursor: pointer;\n" +
                "            transition: all 0.3s ease;\n" +
                "            display: flex;\n" +
                "            align-items: center;\n" +
                "            justify-content: center;\n" +
                "        }\n" +
                "        .generate-btn:hover:not(:disabled) {\n" +
                "            transform: translateY(-2px);\n" +
                "            box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);\n" +
                "        }\n" +
                "        .generate-btn:disabled {\n" +
                "            opacity: 0.7;\n" +
                "            cursor: not-allowed;\n" +
                "        }\n" +
                "        .loading-content {\n" +
                "            display: flex;\n" +
                "            align-items: center;\n" +
                "            gap: 12px;\n" +
                "        }\n" +
                "        .spinner {\n" +
                "            width: 20px;\n" +
                "            height: 20px;\n" +
                "            border: 3px solid rgba(255, 255, 255, 0.3);\n" +
                "            border-top: 3px solid white;\n" +
                "            border-radius: 50%;\n" +
                "            animation: spin 1s linear infinite;\n" +
                "        }\n" +
                "        @keyframes spin {\n" +
                "            0% { transform: rotate(0deg); }\n" +
                "            100% { transform: rotate(360deg); }\n" +
                "        }\n" +
                "        .loading-tips {\n" +
                "            margin-top: 24px;\n" +
                "            text-align: center;\n" +
                "        }\n" +
                "        .current-tip {\n" +
                "            font-size: 16px;\n" +
                "            color: #666;\n" +
                "            margin-bottom: 16px;\n" +
                "            display: block;\n" +
                "        }\n" +
                "        .progress-bar {\n" +
                "            width: 100%;\n" +
                "            height: 6px;\n" +
                "            background: #f0f0f0;\n" +
                "            border-radius: 3px;\n" +
                "            overflow: hidden;\n" +
                "        }\n" +
                "        .progress-fill {\n" +
                "            height: 100%;\n" +
                "            background: linear-gradient(90deg, #667eea, #764ba2);\n" +
                "            border-radius: 3px;\n" +
                "            transition: width 0.3s ease;\n" +
                "        }\n" +
                "        .modal {\n" +
                "            display: none;\n" +
                "            position: fixed;\n" +
                "            top: 0;\n" +
                "            left: 0;\n" +
                "            width: 100%;\n" +
                "            height: 100%;\n" +
                "            background: rgba(0, 0, 0, 0.5);\n" +
                "            z-index: 1000;\n" +
                "            animation: fadeIn 0.3s ease;\n" +
                "        }\n" +
                "        .modal.show {\n" +
                "            display: flex;\n" +
                "            align-items: center;\n" +
                "            justify-content: center;\n" +
                "        }\n" +
                "        @keyframes fadeIn {\n" +
                "            from { opacity: 0; }\n" +
                "            to { opacity: 1; }\n" +
                "        }\n" +
                "        .preview-content {\n" +
                "            width: 95%;\n" +
                "            max-width: 800px;\n" +
                "            height: 90%;\n" +
                "            background: white;\n" +
                "            border-radius: 24px;\n" +
                "            display: flex;\n" +
                "            flex-direction: column;\n" +
                "            animation: slideUp 0.3s ease;\n" +
                "        }\n" +
                "        @keyframes slideUp {\n" +
                "            from { transform: translateY(50px); opacity: 0; }\n" +
                "            to { transform: translateY(0); opacity: 1; }\n" +
                "        }\n" +
                "        .preview-header {\n" +
                "            display: flex;\n" +
                "            justify-content: space-between;\n" +
                "            align-items: center;\n" +
                "            padding: 24px 32px;\n" +
                "            border-bottom: 2px solid #f0f0f0;\n" +
                "        }\n" +
                "        .preview-title {\n" +
                "            font-size: 24px;\n" +
                "            font-weight: 600;\n" +
                "            color: #333;\n" +
                "        }\n" +
                "        .close-btn {\n" +
                "            width: 36px;\n" +
                "            height: 36px;\n" +
                "            background: #f0f0f0;\n" +
                "            border-radius: 50%;\n" +
                "            display: flex;\n" +
                "            align-items: center;\n" +
                "            justify-content: center;\n" +
                "            font-size: 24px;\n" +
                "            color: #666;\n" +
                "            cursor: pointer;\n" +
                "            border: none;\n" +
                "        }\n" +
                "        .preview-body {\n" +
                "            flex: 1;\n" +
                "            padding: 32px;\n" +
                "            overflow-y: auto;\n" +
                "        }\n" +
                "        .preview-footer {\n" +
                "            display: flex;\n" +
                "            gap: 16px;\n" +
                "            padding: 24px 32px;\n" +
                "            border-top: 2px solid #f0f0f0;\n" +
                "        }\n" +
                "        .preview-btn {\n" +
                "            flex: 1;\n" +
                "            height: 48px;\n" +
                "            border-radius: 24px;\n" +
                "            font-size: 16px;\n" +
                "            font-weight: 600;\n" +
                "            border: none;\n" +
                "            cursor: pointer;\n" +
                "        }\n" +
                "        .preview-btn.secondary {\n" +
                "            background: #f0f0f0;\n" +
                "            color: #666;\n" +
                "        }\n" +
                "        .preview-btn.primary {\n" +
                "            background: linear-gradient(135deg, #667eea, #764ba2);\n" +
                "            color: white;\n" +
                "        }\n" +
                "        /* 简历样式 */\n" +
                "        .resume-render {\n" +
                "            font-family: 'Microsoft YaHei', sans-serif;\n" +
                "            line-height: 1.6;\n" +
                "            color: #333;\n" +
                "        }\n" +
                "        .resume-section {\n" +
                "            margin-bottom: 32px;\n" +
                "        }\n" +
                "        .resume-section:last-child {\n" +
                "            margin-bottom: 0;\n" +
                "        }\n" +
                "        .section-header {\n" +
                "            margin-bottom: 16px;\n" +
                "            padding-bottom: 8px;\n" +
                "            border-bottom: 3px solid #667eea;\n" +
                "        }\n" +
                "        .section-title {\n" +
                "            font-size: 24px;\n" +
                "            font-weight: bold;\n" +
                "            color: #333;\n" +
                "        }\n" +
                "        .name-title {\n" +
                "            font-size: 32px;\n" +
                "            font-weight: bold;\n" +
                "            color: #333;\n" +
                "            margin-bottom: 16px;\n" +
                "            text-align: center;\n" +
                "        }\n" +
                "        .contact-info {\n" +
                "            display: flex;\n" +
                "            flex-wrap: wrap;\n" +
                "            gap: 16px;\n" +
                "            justify-content: center;\n" +
                "            margin-bottom: 24px;\n" +
                "        }\n" +
                "        .contact-item {\n" +
                "            display: flex;\n" +
                "            align-items: center;\n" +
                "            font-size: 16px;\n" +
                "        }\n" +
                "        .contact-label {\n" +
                "            color: #666;\n" +
                "            margin-right: 4px;\n" +
                "        }\n" +
                "        .contact-value {\n" +
                "            color: #333;\n" +
                "            font-weight: 500;\n" +
                "        }\n" +
                "        .summary-text {\n" +
                "            font-size: 16px;\n" +
                "            line-height: 1.8;\n" +
                "            color: #333;\n" +
                "            text-align: justify;\n" +
                "        }\n" +
                "        .skills-grid {\n" +
                "            display: flex;\n" +
                "            flex-wrap: wrap;\n" +
                "            gap: 12px;\n" +
                "        }\n" +
                "        .skill-tag {\n" +
                "            background: linear-gradient(135deg, #667eea, #764ba2);\n" +
                "            color: white;\n" +
                "            padding: 8px 16px;\n" +
                "            border-radius: 20px;\n" +
                "            font-size: 14px;\n" +
                "            font-weight: 500;\n" +
                "        }\n" +
                "        .education-item,\n" +
                "        .work-item,\n" +
                "        .project-item {\n" +
                "            margin-bottom: 24px;\n" +
                "            padding: 16px;\n" +
                "            border-left: 4px solid #667eea;\n" +
                "            background: #f8f9fa;\n" +
                "            border-radius: 8px;\n" +
                "        }\n" +
                "        .edu-header,\n" +
                "        .work-header,\n" +
                "        .project-header {\n" +
                "            display: flex;\n" +
                "            justify-content: space-between;\n" +
                "            align-items: center;\n" +
                "            margin-bottom: 8px;\n" +
                "        }\n" +
                "        .edu-school,\n" +
                "        .work-company,\n" +
                "        .project-name {\n" +
                "            font-size: 20px;\n" +
                "            font-weight: bold;\n" +
                "            color: #333;\n" +
                "        }\n" +
                "        .edu-duration,\n" +
                "        .work-duration,\n" +
                "        .project-duration {\n" +
                "            font-size: 14px;\n" +
                "            color: #666;\n" +
                "        }\n" +
                "        .edu-major,\n" +
                "        .work-position {\n" +
                "            font-size: 16px;\n" +
                "            color: #667eea;\n" +
                "            font-weight: 600;\n" +
                "            margin-bottom: 8px;\n" +
                "        }\n" +
                "        .work-description,\n" +
                "        .project-description {\n" +
                "            font-size: 14px;\n" +
                "            color: #555;\n" +
                "            line-height: 1.6;\n" +
                "            margin-bottom: 8px;\n" +
                "        }\n" +
                "        .tech-tags {\n" +
                "            display: flex;\n" +
                "            flex-wrap: wrap;\n" +
                "            gap: 8px;\n" +
                "            margin: 8px 0;\n" +
                "        }\n" +
                "        .tech-tag {\n" +
                "            background: #f0f2f5;\n" +
                "            color: #666;\n" +
                "            padding: 4px 8px;\n" +
                "            border-radius: 12px;\n" +
                "            font-size: 12px;\n" +
                "        }\n" +
                "        .achievement-item,\n" +
                "        .certificate-item,\n" +
                "        .language-item {\n" +
                "            font-size: 14px;\n" +
                "            color: #555;\n" +
                "            margin-bottom: 4px;\n" +
                "            line-height: 1.6;\n" +
                "        }\n" +
                "        /* 响应式设计 */\n" +
                "        @media (max-width: 768px) {\n" +
                "            body {\n" +
                "                padding: 10px;\n" +
                "            }\n" +
                "            .content {\n" +
                "                padding: 20px;\n" +
                "            }\n" +
                "            .template-grid {\n" +
                "                grid-template-columns: 1fr;\n" +
                "            }\n" +
                "            .contact-info {\n" +
                "                flex-direction: column;\n" +
                "                align-items: center;\n" +
                "                gap: 8px;\n" +
                "            }\n" +
                "            .edu-header,\n" +
                "            .work-header,\n" +
                "            .project-header {\n" +
                "                flex-direction: column;\n" +
                "                align-items: flex-start;\n" +
                "                gap: 4px;\n" +
                "            }\n" +
                "        }\n" +
                "    </style>\n" +
                "</head>\n" +
                "<body>\n" +
                "    <div class=\"container\">\n" +
                "        <!-- 顶部标题栏 -->\n" +
                "        <div class=\"header\">\n" +
                "            <h1 class=\"title\">AI 简历生成</h1>\n" +
                "            <p class=\"subtitle\">基于智能分析，生成专业简历</p>\n" +
                "        </div>\n" +
                "        <!-- 主要内容区域 -->\n" +
                "        <div class=\"content\">\n" +
                "            <!-- 表单区域 -->\n" +
                "            <form id=\"resumeForm\">\n" +
                "                <!-- 分析ID输入 -->\n" +
                "                <div class=\"form-group\">\n" +
                "                    <div class=\"label\">\n" +
                "                        <span class=\"label-text\">分析ID</span>\n" +
                "                        <span class=\"required\">*</span>\n" +
                "                    </div>\n" +
                "                    <input class=\"input\" type=\"number\" id=\"analysisId\" placeholder=\"请输入分析ID\" value=\"12345\" required>\n" +
                "                    <span class=\"error-text\" id=\"analysisIdError\" style=\"display: none;\"></span>\n" +
                "                </div>\n" +
                "                <!-- 个人信息输入 -->\n" +
                "                <div class=\"form-group\">\n" +
                "                    <div class=\"label\">\n" +
                "                        <span class=\"label-text\">个人信息</span>\n" +
                "                        <span class=\"required\">*</span>\n" +
                "                    </div>\n" +
                "                    <textarea class=\"textarea\" id=\"personalInfo\" placeholder=\"请输入您的个人信息，包括姓名、联系方式、教育背景、技能特长、项目经验等\" maxlength=\"2000\">张三，前端开发工程师，5年经验。精通React、Vue、TypeScript，熟悉Node.js与Webpack构建优化。曾主导企业级中后台系统重构，性能提升50%；设计并维护组件库，提升团队效率30%。北京邮电大学计算机科学与技术本科，GPA 3.8/4.0。期望加入技术驱动的创新团队，共同打造高质量产品。</textarea>\n" +
                "                    <div class=\"char-count\" id=\"personalInfoCount\">0/2000</div>\n" +
                "                    <span class=\"error-text\" id=\"personalInfoError\" style=\"display: none;\"></span>\n" +
                "                </div>\n" +
                "                <!-- 目标职位输入 -->\n" +
                "                <div class=\"form-group\">\n" +
                "                    <div class=\"label\">\n" +
                "                        <span class=\"label-text\">目标职位</span>\n" +
                "                        <span class=\"required\">*</span>\n" +
                "                    </div>\n" +
                "                    <input class=\"input\" id=\"targetPosition\" placeholder=\"如：前端开发工程师、产品经理等\" value=\"高级前端开发工程师\" required>\n" +
                "                    <span class=\"error-text\" id=\"targetPositionError\" style=\"display: none;\"></span>\n" +
                "                </div>\n" +
                "                <!-- 模板选择 -->\n" +
                "                <div class=\"form-group\">\n" +
                "                    <div class=\"label\">\n" +
                "                        <span class=\"label-text\">简历模板</span>\n" +
                "                        <span class=\"required\">*</span>\n" +
                "                    </div>\n" +
                "                    <div class=\"template-grid\" id=\"templateGrid\">\n" +
                "                        <!-- 模板选项将通过JavaScript生成 -->\n" +
                "                    </div>\n" +
                "                </div>\n" +
                "                <!-- 特殊要求 -->\n" +
                "                <div class=\"form-group\">\n" +
                "                    <div class=\"label\">\n" +
                "                        <span class=\"label-text\">特殊要求</span>\n" +
                "                        <span class=\"optional\">(可选)</span>\n" +
                "                    </div>\n" +
                "                    <textarea class=\"textarea\" id=\"specialRequirements\" placeholder=\"如有特殊格式要求或重点突出的内容，请在此说明\" maxlength=\"500\">1. 突出React与TypeScript经验；2. 强调性能优化与组件库设计成果；3. 简历整体保持简洁专业，不超过两页。</textarea>\n" +
                "                    <div class=\"char-count\" id=\"specialRequirementsCount\">0/500</div>\n" +
                "                </div>\n" +
                "                <!-- 高级设置 -->\n" +
                "                <div class=\"form-group\">\n" +
                "                    <div class=\"advanced-toggle\" id=\"advancedToggle\">\n" +
                "                        <span class=\"advanced-title\">高级设置</span>\n" +
                "                        <span class=\"toggle-icon\">▼</span>\n" +
                "                    </div>\n" +
                "                    \n" +
                "                    <div class=\"advanced-settings\" id=\"advancedSettings\" style=\"display: none;\">\n" +
                "                        <!-- AI模型选择 -->\n" +
                "                        <div class=\"setting-item\">\n" +
                "                            <label class=\"setting-label\">AI模型</label>\n" +
                "                            <select id=\"modelSelect\">\n" +
                "                                <option value=\"17\">Deepseek - 最强大的AI模型，生成质量最高</option>\n" +
                "                                <option value=\"2\">GPT-3.5 - 平衡性能和速度的优秀选择</option>\n" +
                "                                <option value=\"3\">Claude - 擅长长文本生成和逻辑分析</option>\n" +
                "                            </select>\n" +
                "                        </div>\n" +
                "                        <!-- 创意度设置 -->\n" +
                "                        <div class=\"setting-item\">\n" +
                "                            <label class=\"setting-label\">创意度 (<span id=\"temperatureValue\">0.7</span>)</label>\n" +
                "                            <div class=\"slider-container\">\n" +
                "                                <input type=\"range\" class=\"slider\" id=\"temperatureSlider\" min=\"0.1\" max=\"1.0\" step=\"0.1\" value=\"0.7\">\n" +
                "                            </div>\n" +
                "                            <div class=\"slider-labels\">\n" +
                "                                <span class=\"slider-label\">保守</span>\n" +
                "                                <span class=\"slider-label\">创新</span>\n" +
                "                            </div>\n" +
                "                        </div>\n" +
                "                        <!-- 最大字数设置 -->\n" +
                "                        <div class=\"setting-item\">\n" +
                "                            <label class=\"setting-label\">最大字数</label>\n" +
                "                            <select id=\"tokensSelect\">\n" +
                "                                <option value=\"1000\">1000字 (简洁版)</option>\n" +
                "                                <option value=\"1500\">1500字 (标准版)</option>\n" +
                "                                <option value=\"2000\" selected>2000字 (详细版)</option>\n" +
                "                                <option value=\"2500\">2500字 (完整版)</option>\n" +
                "                            </select>\n" +
                "                        </div>\n" +
                "                    </div>\n" +
                "                </div>\n" +
                "            </form>\n" +
                "            <!-- 生成按钮 -->\n" +
                "            <div class=\"generate-section\">\n" +
                "                <button class=\"generate-btn\" id=\"generateBtn\" type=\"button\">\n" +
                "                    <span>生成简历</span>\n" +
                "                </button>\n" +
                "            </div>\n" +
                "            <!-- 加载提示 -->\n" +
                "            <div class=\"loading-tips\" id=\"loadingTips\" style=\"display: none;\">\n" +
                "                <span class=\"current-tip\" id=\"currentTip\">正在分析您的个人信息...</span>\n" +
                "                <div class=\"progress-bar\">\n" +
                "                    <div class=\"progress-fill\" id=\"progressFill\"></div>\n" +
                "                </div>\n" +
                "            </div>\n" +
                "        </div>\n" +
                "        <!-- 简历预览模态框 -->\n" +
                "        <div class=\"modal\" id=\"previewModal\">\n" +
                "            <div class=\"preview-content\">\n" +
                "                <div class=\"preview-header\">\n" +
                "                    <h2 class=\"preview-title\">简历预览</h2>\n" +
                "                    <button class=\"close-btn\" id=\"closeBtn\">×</button>\n" +
                "                </div>\n" +
                "                \n" +
                "                <div class=\"preview-body\">\n" +
                "                    <div class=\"resume-render\" id=\"resumeRender\">\n" +
                "                        <!-- 简历内容将通过JavaScript生成 -->\n" +
                "                    </div>\n" +
                "                </div>\n" +
                "                \n" +
                "                <div class=\"preview-footer\">\n" +
                "                    <button class=\"preview-btn secondary\" id=\"editBtn\">重新生成</button>\n" +
                "                    <button class=\"preview-btn primary\" id=\"downloadBtn\">下载简历</button>\n" +
                "                </div>\n" +
                "            </div>\n" +
                "        </div>\n" +
                "    </div>\n" +
                "    <script>\n" +
                "        // 全局变量\n" +
                "        const templates = [\n" +
                "            { \n" +
                "                id: 1, \n" +
                "                name: '经典商务模板', \n" +
                "                description: '适合传统行业和正式场合，格式规范专业',\n" +
                "                tags: ['正式', '传统', '商务']\n" +
                "            },\n" +
                "            { \n" +
                "                id: 2, \n" +
                "                name: '现代简约模板', \n" +
                "                description: '简洁明了的设计风格，突出重点信息',\n" +
                "                tags: ['简约', '现代', '清晰']\n" +
                "            },\n" +
                "            { \n" +
                "                id: 3, \n" +
                "                name: '创意设计模板', \n" +
                "                description: '适合设计、创意类职位，视觉效果突出',\n" +
                "                tags: ['创意', '设计', '个性']\n" +
                "            },\n" +
                "            { \n" +
                "                id: 4, \n" +
                "                name: '技术专业模板', \n" +
                "                description: '专为技术岗位设计，突出技能和项目经验',\n" +
                "                tags: ['技术', '专业', '项目']\n" +
                "            }\n" +
                "        ];\n" +
                "        \n" +
                "        const loadingTips = [\n" +
                "            '正在分析您的个人信息...',\n" +
                "            '正在匹配最适合的简历模板...',\n" +
                "            '正在优化简历内容结构...',\n" +
                "            '正在生成专业的简历描述...',\n" +
                "            '正在进行最后的格式调整...',\n" +
                "            '简历生成即将完成，请稍候...'\n" +
                "        ];\n" +
                "        \n" +
                "        let selectedTemplate = 1;\n" +
                "        let currentTipIndex = 0;\n" +
                "        let tipTimer = null;\n" +
                "        let resumeData = null;\n" +
                "        \n" +
                "        // 初始化页面\n" +
                "        function initializePage() {\n" +
                "            renderTemplates();\n" +
                "            bindEvents();\n" +
                "            startTipRotation();\n" +
                "        }\n" +
                "        \n" +
                "        // 渲染模板选择\n" +
                "        function renderTemplates() {\n" +
                "            const templateGrid = document.getElementById('templateGrid');\n" +
                "            templateGrid.innerHTML = templates.map(template => `\n" +
                "                <div class=\"template-card ${template.id === selectedTemplate ? 'selected' : ''}\" \n" +
                "                     data-template-id=\"${template.id}\">\n" +
                "                    <div class=\"template-header\">\n" +
                "                        <span class=\"template-name\">${template.name}</span>\n" +
                "                        ${template.id === selectedTemplate ? '<div class=\"selected-icon\">✓</div>' : ''}\n" +
                "                    </div>\n" +
                "                    <span class=\"template-desc\">${template.description}</span>\n" +
                "                    <div class=\"template-tags\">\n" +
                "                        ${template.tags.map(tag => `<span class=\"tag\">${tag}</span>`).join('')}\n" +
                "                    </div>\n" +
                "                </div>\n" +
                "            `).join('');\n" +
                "        }\n" +
                "        \n" +
                "        // 绑定事件\n" +
                "        function bindEvents() {\n" +
                "            // 表单输入事件\n" +
                "            document.getElementById('personalInfo').addEventListener('input', updateCharCount);\n" +
                "            document.getElementById('specialRequirements').addEventListener('input', updateCharCount);\n" +
                "            \n" +
                "            // 高级设置切换\n" +
                "            document.getElementById('advancedToggle').addEventListener('click', toggleAdvanced);\n" +
                "            \n" +
                "            // 温度滑块\n" +
                "            document.getElementById('temperatureSlider').addEventListener('input', updateTemperature);\n" +
                "            \n" +
                "            // 模板选择\n" +
                "            document.addEventListener('click', handleTemplateClick);\n" +
                "            \n" +
                "            // 生成按钮\n" +
                "            document.getElementById('generateBtn').addEventListener('click', generateResume);\n" +
                "            \n" +
                "            // 模态框事件\n" +
                "            document.getElementById('closeBtn').addEventListener('click', closePreview);\n" +
                "            document.getElementById('editBtn').addEventListener('click', editResume);\n" +
                "            document.getElementById('downloadBtn').addEventListener('click', downloadResume);\n" +
                "            \n" +
                "            // 点击模态框外部关闭\n" +
                "            document.getElementById('previewModal').addEventListener('click', (e) => {\n" +
                "                if (e.target.id === 'previewModal') {\n" +
                "                    closePreview();\n" +
                "                }\n" +
                "            });\n" +
                "        }\n" +
                "        \n" +
                "        // 更新字符计数\n" +
                "        function updateCharCount(e) {\n" +
                "            const textarea = e.target;\n" +
                "            const maxLength = parseInt(textarea.getAttribute('maxlength'));\n" +
                "            const currentLength = textarea.value.length;\n" +
                "            const countElement = document.getElementById(textarea.id + 'Count');\n" +
                "            if (countElement) {\n" +
                "                countElement.textContent = `${currentLength}/${maxLength}`;\n" +
                "            }\n" +
                "        }\n" +
                "        \n" +
                "        // 切换高级设置\n" +
                "        function toggleAdvanced() {\n" +
                "            const advancedSettings = document.getElementById('advancedSettings');\n" +
                "            const toggleIcon = document.querySelector('.toggle-icon');\n" +
                "            \n" +
                "            if (advancedSettings.style.display === 'none') {\n" +
                "                advancedSettings.style.display = 'block';\n" +
                "                toggleIcon.textContent = '▲';\n" +
                "            } else {\n" +
                "                advancedSettings.style.display = 'none';\n" +
                "                toggleIcon.textContent = '▼';\n" +
                "            }\n" +
                "        }\n" +
                "        \n" +
                "        // 更新温度值\n" +
                "        function updateTemperature(e) {\n" +
                "            document.getElementById('temperatureValue').textContent = e.target.value;\n" +
                "        }\n" +
                "        \n" +
                "        // 处理模板选择\n" +
                "        function handleTemplateClick(e) {\n" +
                "            const card = e.target.closest('.template-card');\n" +
                "            if (card) {\n" +
                "                selectedTemplate = parseInt(card.dataset.templateId);\n" +
                "                renderTemplates();\n" +
                "            }\n" +
                "        }\n" +
                "        \n" +
                "        // 开始提示轮播\n" +
                "        function startTipRotation() {\n" +
                "            if (tipTimer) clearInterval(tipTimer);\n" +
                "            tipTimer = setInterval(() => {\n" +
                "                currentTipIndex = (currentTipIndex + 1) % loadingTips.length;\n" +
                "                const currentTip = document.getElementById('currentTip');\n" +
                "                if (currentTip) {\n" +
                "                    currentTip.textContent = loadingTips[currentTipIndex];\n" +
                "                }\n" +
                "            }, 3000);\n" +
                "        }\n" +
                "        \n" +
                "        // 表单验证\n" +
                "        function validateForm() {\n" +
                "            const errors = {};\n" +
                "            let isValid = true;\n" +
                "            \n" +
                "            const analysisId = document.getElementById('analysisId').value;\n" +
                "            const personalInfo = document.getElementById('personalInfo').value.trim();\n" +
                "            const targetPosition = document.getElementById('targetPosition').value.trim();\n" +
                "            \n" +
                "            // 清除之前的错误显示\n" +
                "            document.querySelectorAll('.error-text').forEach(el => el.style.display = 'none');\n" +
                "            document.querySelectorAll('.input, .textarea').forEach(el => el.classList.remove('error'));\n" +
                "            \n" +
                "            if (!analysisId) {\n" +
                "                errors.analysisId = '请输入分析ID';\n" +
                "                showError('analysisId', errors.analysisId);\n" +
                "                isValid = false;\n" +
                "            }\n" +
                "            \n" +
                "            if (!personalInfo) {\n" +
                "                errors.personalInfo = '请输入个人信息';\n" +
                "                showError('personalInfo', errors.personalInfo);\n" +
                "                isValid = false;\n" +
                "            } else if (personalInfo.length < 50) {\n" +
                "                errors.personalInfo = '个人信息至少需要50个字符';\n" +
                "                showError('personalInfo', errors.personalInfo);\n" +
                "                isValid = false;\n" +
                "            }\n" +
                "            \n" +
                "            if (!targetPosition) {\n" +
                "                errors.targetPosition = '请输入目标职位';\n" +
                "                showError('targetPosition', errors.targetPosition);\n" +
                "                isValid = false;\n" +
                "            }\n" +
                "            \n" +
                "            return isValid;\n" +
                "        }\n" +
                "        \n" +
                "        // 显示错误\n" +
                "        function showError(fieldId, message) {\n" +
                "            const field = document.getElementById(fieldId);\n" +
                "            const errorText = document.getElementById(fieldId + 'Error');\n" +
                "            if (field) field.classList.add('error');\n" +
                "            if (errorText) {\n" +
                "                errorText.textContent = message;\n" +
                "                errorText.style.display = 'block';\n" +
                "            }\n" +
                "        }\n" +
                "        \n" +
                "        // 生成简历\n" +
                "        async function generateResume() {\n" +
                "            if (!validateForm()) {\n" +
                "                alert('请检查必填项并填写完整');\n" +
                "                return;\n" +
                "            }\n" +
                "            \n" +
                "            const generateBtn = document.getElementById('generateBtn');\n" +
                "            const loadingTips = document.getElementById('loadingTips');\n" +
                "            const progressFill = document.getElementById('progressFill');\n" +
                "            \n" +
                "            generateBtn.disabled = true;\n" +
                "            generateBtn.innerHTML = `\n" +
                "                <div class=\"loading-content\">\n" +
                "                    <div class=\"spinner\"></div>\n" +
                "                    <span class=\"loading-text\">正在生成中...</span>\n" +
                "                </div>\n" +
                "            `;\n" +
                "            loadingTips.style.display = 'block';\n" +
                "            \n" +
                "            let progress = 0;\n" +
                "            const progressInterval = setInterval(() => {\n" +
                "                progress += Math.random() * 15;\n" +
                "                if (progress > 90) progress = 90;\n" +
                "                progressFill.style.width = progress + '%';\n" +
                "            }, 500);\n" +
                "            \n" +
                "            // 模拟API调用\n" +
                "            setTimeout(() => {\n" +
                "                clearInterval(progressInterval);\n" +
                "                progressFill.style.width = '100%';\n" +
                "                \n" +
                "                setTimeout(() => {\n" +
                "                    // 模拟生成的简历数据\n" +
                "                    resumeData = generateMockResumeData();\n" +
                "                    renderResume(resumeData);\n" +
                "                    showPreview();\n" +
                "                    \n" +
                "                    generateBtn.disabled = false;\n" +
                "                    generateBtn.innerHTML = '<span>生成简历</span>';\n" +
                "                    loadingTips.style.display = 'none';\n" +
                "                    progressFill.style.width = '0%';\n" +
                "                }, 500);\n" +
                "            }, 3000);\n" +
                "        }\n" +
                "        \n" +
                "        // 生成模拟简历数据\n" +
                "        function generateMockResumeData() {\n" +
                "            return {\n" +
                "                personalInfo: {\n" +
                "                    name: '张三',\n" +
                "                    phone: '138-0000-0000',\n" +
                "                    email: 'zhangsan@example.com',\n" +
                "                    location: '北京市朝阳区'\n" +
                "                },\n" +
                "                summary: '具有5年前端开发经验的资深工程师，精通React、Vue等主流框架，擅长构建高性能、可扩展的Web应用。在多个大型项目中担任核心开发角色，具备优秀的团队协作和项目管理能力。',\n" +
                "                skills: ['JavaScript', 'React', 'Vue.js', 'Node.js', 'TypeScript', 'Webpack', 'Git', 'RESTful API'],\n" +
                "                education: [\n" +
                "                    {\n" +
                "                        school: '北京大学',\n" +
                "                        duration: '2015-2019',\n" +
                "                        major: '计算机科学与技术',\n" +
                "                        degree: '本科',\n" +
                "                        gpa: 'GPA: 3.8/4.0',\n" +
                "                        achievements: ['获得校级优秀毕业生', '参与ACM程序设计竞赛']\n" +
                "                    }\n" +
                "                ],\n" +
                "                workExperience: [\n" +
                "                    {\n" +
                "                        company: '某知名互联网公司',\n" +
                "                        duration: '2019-至今',\n" +
                "                        position: '高级前端开发工程师',\n" +
                "                        description: '负责公司核心产品的前端架构设计和开发工作',\n" +
                "                        achievements: [\n" +
                "                            '主导完成了公司官网的重构，性能提升50%',\n" +
                "                            '设计并实现了组件库，提高了团队开发效率30%',\n" +
                "                            '参与制定了前端开发规范，提升了代码质量'\n" +
                "                        ]\n" +
                "                    }\n" +
                "                ],\n" +
                "                projects: [\n" +
                "                    {\n" +
                "                        name: '企业级中后台管理系统',\n" +
                "                        duration: '2022-2023',\n" +
                "                        description: '为公司内部开发的综合管理系统，集成了用户管理、权限控制、数据分析等功能',\n" +
                "                        technologies: ['React', 'Ant Design', 'TypeScript', 'Node.js', 'MongoDB'],\n" +
                "                        achievements: [\n" +
                "                            '采用微前端架构，支持多团队并行开发',\n" +
                "                            '实现了响应式设计，兼容PC和移动端',\n" +
                "                            '通过代码分割优化，首屏加载时间减少40%'\n" +
                "                        ]\n" +
                "                    }\n" +
                "                ],\n" +
                "                certificates: ['前端开发工程师认证', '阿里云ACA认证', '腾讯云TCA认证'],\n" +
                "                languages: ['英语 - CET-6', '普通话 - 母语']\n" +
                "            };\n" +
                "        }\n" +
                "        \n" +
                "        // 渲染简历\n" +
                "        function renderResume(data) {\n" +
                "            const resumeRender = document.getElementById('resumeRender');\n" +
                "            resumeRender.innerHTML = `\n" +
                "                <!-- 个人信息部分 -->\n" +
                "                <div class=\"resume-section\">\n" +
                "                    <div class=\"section-header\">\n" +
                "                        <h3 class=\"section-title\">个人信息</h3>\n" +
                "                    </div>\n" +
                "                    <div class=\"section-content\">\n" +
                "                        <h1 class=\"name-title\">${data.personalInfo.name}</h1>\n" +
                "                        <div class=\"contact-info\">\n" +
                "                            ${data.personalInfo.phone ? `<div class=\"contact-item\">\n" +
                "                                <span class=\"contact-label\">电话：</span>\n" +
                "                                <span class=\"contact-value\">${data.personalInfo.phone}</span>\n" +
                "                            </div>` : ''}\n" +
                "                            ${data.personalInfo.email ? `<div class=\"contact-item\">\n" +
                "                                <span class=\"contact-label\">邮箱：</span>\n" +
                "                                <span class=\"contact-value\">${data.personalInfo.email}</span>\n" +
                "                            </div>` : ''}\n" +
                "                            ${data.personalInfo.location ? `<div class=\"contact-item\">\n" +
                "                                <span class=\"contact-label\">地址：</span>\n" +
                "                                <span class=\"contact-value\">${data.personalInfo.location}</span>\n" +
                "                            </div>` : ''}\n" +
                "                        </div>\n" +
                "                    </div>\n" +
                "                </div>\n" +
                "                \n" +
                "                <!-- 个人简介 -->\n" +
                "                ${data.summary ? `<div class=\"resume-section\">\n" +
                "                    <div class=\"section-header\">\n" +
                "                        <h3 class=\"section-title\">个人简介</h3>\n" +
                "                    </div>\n" +
                "                    <div class=\"section-content\">\n" +
                "                        <p class=\"summary-text\">${data.summary}</p>\n" +
                "                    </div>\n" +
                "                </div>` : ''}\n" +
                "                \n" +
                "                <!-- 技能专长 -->\n" +
                "                ${data.skills && data.skills.length > 0 ? `<div class=\"resume-section\">\n" +
                "                    <div class=\"section-header\">\n" +
                "                        <h3 class=\"section-title\">技能专长</h3>\n" +
                "                    </div>\n" +
                "                    <div class=\"section-content\">\n" +
                "                        <div class=\"skills-grid\">\n" +
                "                            ${data.skills.map(skill => `<span class=\"skill-tag\">${skill}</span>`).join('')}\n" +
                "                        </div>\n" +
                "                    </div>\n" +
                "                </div>` : ''}\n" +
                "                \n" +
                "                <!-- 教育背景 -->\n" +
                "                ${data.education && data.education.length > 0 ? `<div class=\"resume-section\">\n" +
                "                    <div class=\"section-header\">\n" +
                "                        <h3 class=\"section-title\">教育背景</h3>\n" +
                "                    </div>\n" +
                "                    <div class=\"section-content\">\n" +
                "                        ${data.education.map(edu => `\n" +
                "                            <div class=\"education-item\">\n" +
                "                                <div class=\"edu-header\">\n" +
                "                                    <span class=\"edu-school\">${edu.school}</span>\n" +
                "                                    <span class=\"edu-duration\">${edu.duration}</span>\n" +
                "                                </div>\n" +
                "                                <div class=\"edu-details\">\n" +
                "                                    <span class=\"edu-major\">${edu.major} | ${edu.degree}</span>\n" +
                "                                    ${edu.gpa ? `<span class=\"edu-gpa\">${edu.gpa}</span>` : ''}\n" +
                "                                </div>\n" +
                "                                ${edu.achievements && edu.achievements.length > 0 ? `\n" +
                "                                    <div class=\"edu-achievements\">\n" +
                "                                        <strong class=\"achievements-title\">主要成就：</strong>\n" +
                "                                        ${edu.achievements.map(achievement => `\n" +
                "                                            <div class=\"achievement-item\">• ${achievement}</div>\n" +
                "                                        `).join('')}\n" +
                "                                    </div>\n" +
                "                                ` : ''}\n" +
                "                            </div>\n" +
                "                        `).join('')}\n" +
                "                    </div>\n" +
                "                </div>` : ''}\n" +
                "                \n" +
                "                <!-- 工作经历 -->\n" +
                "                ${data.workExperience && data.workExperience.length > 0 ? `<div class=\"resume-section\">\n" +
                "                    <div class=\"section-header\">\n" +
                "                        <h3 class=\"section-title\">工作经历</h3>\n" +
                "                    </div>\n" +
                "                    <div class=\"section-content\">\n" +
                "                        ${data.workExperience.map(work => `\n" +
                "                            <div class=\"work-item\">\n" +
                "                                <div class=\"work-header\">\n" +
                "                                    <span class=\"work-company\">${work.company}</span>\n" +
                "                                    <span class=\"work-duration\">${work.duration}</span>\n" +
                "                                </div>\n" +
                "                                <div class=\"work-position\">${work.position}</div>\n" +
                "                                <div class=\"work-description\">${work.description}</div>\n" +
                "                                ${work.achievements && work.achievements.length > 0 ? `\n" +
                "                                    <div class=\"work-achievements\">\n" +
                "                                        ${work.achievements.map(achievement => `\n" +
                "                                            <div class=\"achievement-item\">• ${achievement}</div>\n" +
                "                                        `).join('')}\n" +
                "                                    </div>\n" +
                "                                ` : ''}\n" +
                "                            </div>\n" +
                "                        `).join('')}\n" +
                "                    </div>\n" +
                "                </div>` : ''}\n" +
                "                \n" +
                "                <!-- 项目经验 -->\n" +
                "                ${data.projects && data.projects.length > 0 ? `<div class=\"resume-section\">\n" +
                "                    <div class=\"section-header\">\n" +
                "                        <h3 class=\"section-title\">项目经验</h3>\n" +
                "                    </div>\n" +
                "                    <div class=\"section-content\">\n" +
                "                        ${data.projects.map(project => `\n" +
                "                            <div class=\"project-item\">\n" +
                "                                <div class=\"project-header\">\n" +
                "                                    <span class=\"project-name\">${project.name}</span>\n" +
                "                                    ${project.duration ? `<span class=\"project-duration\">${project.duration}</span>` : ''}\n" +
                "                                </div>\n" +
                "                                <div class=\"project-description\">${project.description}</div>\n" +
                "                                ${project.technologies && project.technologies.length > 0 ? `\n" +
                "                                    <div class=\"project-tech\">\n" +
                "                                        <strong class=\"tech-title\">技术栈：</strong>\n" +
                "                                        <div class=\"tech-tags\">\n" +
                "                                            ${project.technologies.map(tech => `<span class=\"tech-tag\">${tech}</span>`).join('')}\n" +
                "                                        </div>\n" +
                "                                    </div>\n" +
                "                                ` : ''}\n" +
                "                                ${project.achievements && project.achievements.length > 0 ? `\n" +
                "                                    <div class=\"project-achievements\">\n" +
                "                                        <strong class=\"achievements-title\">主要成果：</strong>\n" +
                "                                        ${project.achievements.map(achievement => `\n" +
                "                                            <div class=\"achievement-item\">• ${achievement}</div>\n" +
                "                                        `).join('')}\n" +
                "                                    </div>\n" +
                "                                ` : ''}\n" +
                "                            </div>\n" +
                "                        `).join('')}\n" +
                "                    </div>\n" +
                "                </div>` : ''}\n" +
                "                \n" +
                "                <!-- 证书资格 -->\n" +
                "                ${data.certificates && data.certificates.length > 0 ? `<div class=\"resume-section\">\n" +
                "                    <div class=\"section-header\">\n" +
                "                        <h3 class=\"section-title\">证书资格</h3>\n" +
                "                    </div>\n" +
                "                    <div class=\"section-content\">\n" +
                "                        ${data.certificates.map(cert => `\n" +
                "                            <div class=\"certificate-item\">• ${cert}</div>\n" +
                "                        `).join('')}\n" +
                "                    </div>\n" +
                "                </div>` : ''}\n" +
                "                \n" +
                "                <!-- 语言能力 -->\n" +
                "                ${data.languages && data.languages.length > 0 ? `<div class=\"resume-section\">\n" +
                "                    <div class=\"section-header\">\n" +
                "                        <h3 class=\"section-title\">语言能力</h3>\n" +
                "                    </div>\n" +
                "                    <div class=\"section-content\">\n" +
                "                        ${data.languages.map(lang => `\n" +
                "                            <div class=\"language-item\">• ${lang}</div>\n" +
                "                        `).join('')}\n" +
                "                    </div>\n" +
                "                </div>` : ''}\n" +
                "            `;\n" +
                "        }\n" +
                "        \n" +
                "        // 显示预览\n" +
                "        function showPreview() {\n" +
                "            document.getElementById('previewModal').classList.add('show');\n" +
                "            document.body.style.overflow = 'hidden';\n" +
                "        }\n" +
                "        \n" +
                "        // 关闭预览\n" +
                "        function closePreview() {\n" +
                "            document.getElementById('previewModal').classList.remove('show');\n" +
                "            document.body.style.overflow = 'auto';\n" +
                "        }\n" +
                "        \n" +
                "        // 重新生成\n" +
                "        function editResume() {\n" +
                "            closePreview();\n" +
                "            // 用户可以修改表单后重新生成\n" +
                "        }\n" +
                "        \n" +
                "        // 下载简历\n" +
                "        function downloadResume() {\n" +
                "            alert('下载功能需要后端支持，这里提供下载提示。\\n\\n实际应用中，这里会调用后端API生成PDF文件并下载。');\n" +
                "            \n" +
                "            // 模拟下载提示\n" +
                "            const blob = new Blob([\n" +
                "                document.getElementById('resumeRender').innerText\n" +
                "            ], { type: 'text/plain' });\n" +
                "            const url = URL.createObjectURL(blob);\n" +
                "            const a = document.createElement('a');\n" +
                "            a.href = url;\n" +
                "            a.download = '简历.txt';\n" +
                "            a.click();\n" +
                "            URL.revokeObjectURL(url);\n" +
                "        }\n" +
                "        \n" +
                "        // 页面加载完成后初始化\n" +
                "        document.addEventListener('DOMContentLoaded', initializePage);\n" +
                "    </script>\n" +
                "</body>\n" +
                "</html>");
        page2.setDescription("AI 简历生成器模块的响应式HTML页面，包含表单输入、模板选择和简历预览");
        page2.setModuleId("M002");
        pages.add(page2);

        // 设置页面数据
        result.setPages(pages);

        // 从文本中识别页数（假设为2页，根据需求调整）
        int pageCount = pages.size();

        // 补充总结信息

        result.setSummary(String.format("开始生成简历生成模块高保真原型", pageCount));

        return result;
    }


    /**
     * 流程化生成项目文件的接口
     *
     * @param description 描述性文字
     * @param previousCode 项目文档
     * @param interfaceWord 接口文档
     * @return 代码生成成功的代码
     */
    @PostMapping("/generate-project-code")
    public AIProcessCode generateProjectCode(
            @RequestParam @NotBlank(message = "描述性文字不能为空") String description,
            @RequestParam("designDocument") MultipartFile previousCode,
            @RequestParam("interfaceDocument") MultipartFile interfaceWord) throws IOException {

        // 1. 基础校验
        String originalFilename = previousCode.getOriginalFilename();
        String originalFilename2 = interfaceWord.getOriginalFilename();
        if (originalFilename == null || originalFilename2 == null) {
            throw new IllegalArgumentException("文件名不能为空");
        }
        String ext = originalFilename.substring(originalFilename.lastIndexOf('.')).toLowerCase();
        if (!List.of(".txt").contains(ext)) {
            throw new IllegalArgumentException("本接口仅支持 .txt 文件");
        }
        //2. 调用方法解析项目文档中的内容
        String previousCodeWord = textExtractor.extract(previousCode);
        //3. 调用方法解析接口文档中的内容
        String interfaceWordWord = textExtractor.extract(interfaceWord);
        AIProcessCode aiProcessCode = new AIProcessCode();

        //4. 页面代码+接口代码=SQL代码
        //组合接口文档和页面代码为 AI 提供提示词
        String SQLcontent = "请根据前端页面代码和接口文档生成SQL代码。接口文档：" + previousCodeWord + "需求文档：" + interfaceWordWord;
        //调用 AI 生成SQL代码
        System.out.println("SQLcontent" +SQLcontent);
        AIProcessCode multiFileCodeResult = aiProcessCodeService.CreateSQLCode(SQLcontent);
        //将代码保存到本地
        aiProcessCode.setSqlCode(String.valueOf(multiFileCodeResult));
        sqlSaveTool.save(multiFileCodeResult);
        //5. sql→do
        //为 AI 组建提示词
        String DOcontent = "请根据SQL代码生成DO类。SQL代码：" + multiFileCodeResult.getSqlCode();
        //调用 AI 生成DO类
        AIProcessCode doCode = aiProcessCodeService.CreateDOCode(DOcontent);
        //将代码保存到本地
        aiProcessCode.setDoCode(String.valueOf(doCode));
        doSaveTool.save(doCode);
        //6. 接口文档→vo
        //为 AI 组建提示词
        String VOcontent = "请根据接口文档生成VO类。接口文档：" + interfaceWordWord;
        //调用 AI 生成VO类
        AIProcessCode voCode = aiProcessCodeService.CreateVOCode(VOcontent);
        //将代码保存到本地
        aiProcessCode.setVoCode(String.valueOf(voCode));
        voSaveTool.save(voCode);
        //7. DO + VO  →  Mapper
        //为 AI 组建提示词
        String MAPPERcontent = "请根据DO类和VO类生成Mapper类。DO类：" + doCode.getDoCode() + "VO类：" + voCode.getVoCode();
        //调用 AI 生成Mapper类
        AIProcessCode mapperCode = aiProcessCodeService.CreateMapperCode(MAPPERcontent);
        //将代码保存到本地
        aiProcessCode.setMapperCode(String.valueOf(mapperCode));
        mapperSaveTool.save(mapperCode);
        //8. VO + Mapper  →  Service 接口
        //为 AI 组建提示词
        String SERVICEcontent = "请根据VO类和Mapper类生成Service接口。VO类：" + voCode.getVoCode() + "Mapper类：" + mapperCode.getMapperCode();
        //调用 AI 生成Service接口
        AIProcessCode serviceCode = aiProcessCodeService.CreateServiceCode(SERVICEcontent);
        //将代码保存到本地
        aiProcessCode.setServiceCode(String.valueOf(serviceCode));
        serviceSaveTool.save(serviceCode);
        //9. Service 接口  →  ServiceImpl
        //为 AI 组建提示词
        String SERVICEIMPLcontent = "请根据Service接口生成ServiceImpl类。Service接口：" + serviceCode.getServiceCode();
        //调用 AI 生成ServiceImpl类
        AIProcessCode serviceImplCode = aiProcessCodeService.CreateServiceImplCode(SERVICEIMPLcontent);
        //将代码保存到本地
        aiProcessCode.setServiceImplCode(String.valueOf(serviceImplCode));
        serviceImplSaveTool.save(serviceImplCode);
        //10. 接口文档 + ServiceImpl  →  Controller
        //为 AI 组建提示词
        String CONTROLLERcontent = "请根据接口文档和ServiceImpl类生成Controller类。接口文档：" + interfaceWordWord + "ServiceImpl类：" + serviceImplCode.getServiceImplCode();
        //调用 AI 生成Controller类
        AIProcessCode controllerCode = aiProcessCodeService.CreateControllerCode(CONTROLLERcontent);
        //将代码保存到本地
        aiProcessCode.setControllerCode(String.valueOf(controllerCode));
        controllerSaveTool.save(controllerCode);
        System.out.println(description);
        //11. 向前端返回内容
        return aiProcessCode;
    }


    /**
     * 流程化生成项目后端文件的接口
     *
     * @param description 描述性文字
     * @param previousCode 项目文档
     * @param interfaceWord 接口文档
     * @return 代码生成成功的代码
     */
    @PostMapping("/generate-project-code1")
    public AIProcessCode generateProjectCod1(
            @RequestParam @NotBlank(message = "描述性文字不能为空") String description,
            @RequestParam("file") MultipartFile previousCode,
            @RequestParam("file") MultipartFile interfaceWord) throws IOException {

        /* 1. 基础校验（保持原样） */
        String originalFilename = previousCode.getOriginalFilename();
        String originalFilename2 = interfaceWord.getOriginalFilename();
        if (originalFilename == null || originalFilename2 == null) {
            throw new IllegalArgumentException("文件名不能为空");
        }
        String ext = originalFilename.substring(originalFilename.lastIndexOf('.')).toLowerCase();
        if (!List.of(".txt").contains(ext)) {
            throw new IllegalArgumentException("本接口仅支持 .txt 文件");
        }

        /* 2. 提取文本（保持原样） */
        String previousCodeWord = textExtractor.extract(previousCode);
        System.out.println("==================================================================================");
        System.out.println("previousCodeWord:" + previousCodeWord);
        System.out.println("==================================================================================");

        String interfaceWordWord = textExtractor.extract(interfaceWord);
        System.out.println("==================================================================================");
        System.out.println("interfaceWordWord:" + interfaceWordWord);
        System.out.println("==================================================================================");

        /* 3. 构造原始 Prompt（保持原样） */
        String SQLcontent = "请根据前端页面代码和接口文档生成SQL代码。前端代码：" + previousCodeWord + "接口文档：" + interfaceWordWord;

        /* 4. RAG 增强：在真正调用 AI 之前，注入相关规范 */
        String enhancedPrompt = promptEnhancer.enhance(SQLcontent);
        System.out.println("enhancedPrompt:" + enhancedPrompt);

        AIProcessCode aiProcessCode = new AIProcessCode();

        /* 5. 生成 SQL（使用增强后的 Prompt） */
        AIProcessCode multiFileCodeResult = aiProcessCodeService.CreateSQLCode(enhancedPrompt);
        aiProcessCode.setSqlCode(String.valueOf(multiFileCodeResult));
        sqlSaveTool.save(multiFileCodeResult);

        /* 6~11. 后续链式生成保持不变（原逻辑） */
        String DOcontent = "请根据SQL代码生成DO类。SQL代码：" + multiFileCodeResult.getSqlCode();
        AIProcessCode doCode = aiProcessCodeService.CreateDOCode(DOcontent);
        aiProcessCode.setDoCode(String.valueOf(doCode));
        doSaveTool.save(doCode);

        String VOcontent = "请根据接口文档生成VO类。接口文档：" + interfaceWordWord;
        AIProcessCode voCode = aiProcessCodeService.CreateVOCode(VOcontent);
        aiProcessCode.setVoCode(String.valueOf(voCode));
        voSaveTool.save(voCode);

        String MAPPERcontent = "请根据DO类和VO类生成Mapper类。DO类：" + doCode.getDoCode() + "VO类：" + voCode.getVoCode();
        AIProcessCode mapperCode = aiProcessCodeService.CreateMapperCode(MAPPERcontent);
        aiProcessCode.setMapperCode(String.valueOf(mapperCode));
        mapperSaveTool.save(mapperCode);

        String SERVICEcontent = "请根据VO类和Mapper类生成Service接口。VO类：" + voCode.getVoCode() + "Mapper类：" + mapperCode.getMapperCode();
        AIProcessCode serviceCode = aiProcessCodeService.CreateServiceCode(SERVICEcontent);
        aiProcessCode.setServiceCode(String.valueOf(serviceCode));
        serviceSaveTool.save(serviceCode);

        String SERVICEIMPLcontent = "请根据Service接口生成ServiceImpl类。Service接口：" + serviceCode.getServiceCode();
        AIProcessCode serviceImplCode = aiProcessCodeService.CreateServiceImplCode(SERVICEIMPLcontent);
        aiProcessCode.setServiceImplCode(String.valueOf(serviceImplCode));
        serviceImplSaveTool.save(serviceImplCode);

        String CONTROLLERcontent = "请根据接口文档和ServiceImpl类生成Controller类。接口文档：" + interfaceWordWord + "ServiceImpl类：" + serviceImplCode.getServiceImplCode();
        AIProcessCode controllerCode = aiProcessCodeService.CreateControllerCode(CONTROLLERcontent);
        aiProcessCode.setControllerCode(String.valueOf(controllerCode));
        controllerSaveTool.save(controllerCode);

        /* 12. 返回结果 */
        return aiProcessCode;
    }


    /**
     * 流程化生成项目代码的接口
     *
     * @param description 描述性文字
     * @param previousCode 项目文档
     * @param interfaceWord 接口文档
     * @return 代码生成成功的代码
     */
    @PostMapping("/generate-project-code2")
    public AIProcessCode generateProjectCode2(
            @RequestParam @NotBlank(message = "描述性文字不能为空") String description,
            @RequestParam("file") MultipartFile previousCode,
            @RequestParam("file") MultipartFile interfaceWord) throws IOException {

        // 1. 基础校验
        String originalFilename = previousCode.getOriginalFilename();
        String originalFilename2 = interfaceWord.getOriginalFilename();
        if (originalFilename == null || originalFilename2 == null) {
            throw new IllegalArgumentException("文件名不能为空");
        }
        String ext = originalFilename.substring(originalFilename.lastIndexOf('.')).toLowerCase();
        if (!List.of(".txt").contains(ext)) {
            throw new IllegalArgumentException("本接口仅支持 .txt 文件");
        }
        //2. 调用方法解析项目文档中的内容
        String previousCodeWord = textExtractor.extract(previousCode);
        System.out.println("==================================================================================");
        System.out.println("previousCodeWord:" + previousCodeWord);
        System.out.println("==================================================================================");
        //3. 调用方法解析接口文档中的内容
        String interfaceWordWord = textExtractor.extract(interfaceWord);
        System.out.println("==================================================================================");
        System.out.println("interfaceWordWord:" + interfaceWordWord);
        System.out.println("==================================================================================");
        AIProcessCode aiProcessCode = new AIProcessCode();

        //4. 页面代码+接口代码=SQL代码
        //组合接口文档和页面代码为 AI 提供提示词
        String SQLcontent = "请根据前端页面代码和接口文档生成SQL代码。前端代码：" + previousCodeWord + "接口文档：" + interfaceWordWord;
        //调用 AI 生成SQL代码
        System.out.println("SQLcontent" +SQLcontent);
        AIProcessCode multiFileCodeResult = aiProcessCodeService.CreateSQLCode(SQLcontent);
        //将代码保存到本地
        aiProcessCode.setSqlCode(String.valueOf(multiFileCodeResult));
//        sqlSaveTool.save(multiFileCodeResult);
        //5. sql→do
        //为 AI 组建提示词
        String DOcontent = "请根据SQL代码生成DO类。SQL代码：" + multiFileCodeResult.getSqlCode();
        //调用 AI 生成DO类
        AIProcessCode doCode = aiProcessCodeService.CreateDOCode(DOcontent);
        //将代码保存到本地
        aiProcessCode.setDoCode(String.valueOf(doCode));
        doSaveTool.save(doCode);
        //6. 接口文档→vo
        //为 AI 组建提示词
        String VOcontent = "请根据接口文档生成VO类。接口文档：" + interfaceWordWord;
        //调用 AI 生成VO类
        AIProcessCode voCode = aiProcessCodeService.CreateVOCode(VOcontent);
        //将代码保存到本地
        aiProcessCode.setVoCode(String.valueOf(voCode));
        voSaveTool.save(voCode);
        //7. DO + VO  →  Mapper
        //为 AI 组建提示词
        String MAPPERcontent = "请根据DO类和VO类生成Mapper类。DO类：" + doCode.getDoCode() + "VO类：" + voCode.getVoCode();
        //调用 AI 生成Mapper类
        AIProcessCode mapperCode = aiProcessCodeService.CreateMapperCode(MAPPERcontent);
        //将代码保存到本地
        aiProcessCode.setMapperCode(String.valueOf(mapperCode));
        mapperSaveTool.save(mapperCode);
        //8. VO + Mapper  →  Service 接口
        //为 AI 组建提示词
        String SERVICEcontent = "请根据VO类和Mapper类生成Service接口。VO类：" + voCode.getVoCode() + "Mapper类：" + mapperCode.getMapperCode();
        //调用 AI 生成Service接口
        AIProcessCode serviceCode = aiProcessCodeService.CreateServiceCode(SERVICEcontent);
        //将代码保存到本地
        aiProcessCode.setServiceCode(String.valueOf(serviceCode));
        serviceSaveTool.save(serviceCode);
        //9. Service 接口  →  ServiceImpl
        //为 AI 组建提示词
        String SERVICEIMPLcontent = "请根据Service接口生成ServiceImpl类。Service接口：" + serviceCode.getServiceCode();
        //调用 AI 生成ServiceImpl类
        AIProcessCode serviceImplCode = aiProcessCodeService.CreateServiceImplCode(SERVICEIMPLcontent);
        //将代码保存到本地
        aiProcessCode.setServiceImplCode(String.valueOf(serviceImplCode));
        serviceImplSaveTool.save(serviceImplCode);
        //10. 接口文档 + ServiceImpl  →  Controller
        //为 AI 组建提示词
        String CONTROLLERcontent = "请根据接口文档和ServiceImpl类生成Controller类。接口文档：" + interfaceWordWord + "ServiceImpl类：" + serviceImplCode.getServiceImplCode();
        //调用 AI 生成Controller类
        AIProcessCode controllerCode = aiProcessCodeService.CreateControllerCode(CONTROLLERcontent);
        //将代码保存到本地
        aiProcessCode.setControllerCode(String.valueOf(controllerCode));
        controllerSaveTool.save(controllerCode);
        //11. 接口文档 + 设计文档 + 前端页面 →  vue页面
        //为 AI 组建提示词
        String VUE1content = "请根据接口文档 + 设计文档 + 前端页面生成前端页面。接口文档：" + interfaceWordWord;
        //调用 AI 生成ServiceImpl类
        AIProcessCode vue1Code = aiProcessCodeService.CreateVueCode(VUE1content);
        //将代码保存到本地
        aiProcessCode.setVueCode1(String.valueOf(vue1Code));
        serviceImplSaveTool.save(vue1Code);
        //12. 接口文档 + 设计文档 + 前端页面 →  vue页面
        //为 AI 组建提示词
        String VUE2content = "请根据接口文档 + 设计文档 + 前端页面生成前端页面。接口文档：" + interfaceWordWord;
        //调用 AI 生成ServiceImpl类
        AIProcessCode vue2Code = aiProcessCodeService.CreateVueCode(VUE2content);
        //将代码保存到本地
        aiProcessCode.setVueCode2(String.valueOf(vue2Code));
        serviceImplSaveTool.save(vue2Code);
        //13. 向前端返回内容
        return aiProcessCode;
    }



    /**
     * 多线程实现同时调用AI模型
     * 并行流水线：一次性生成 SQL/DO/VO/Mapper/Service/ServiceImpl/Controller
     * POST /api/v1/generate-project-code-async
     * Content-Type: multipart/form-data
     * @return 代码生成成功的代码
     */
    @PostMapping("/generate-project-code-async")
    public AIProcessCode generateProjectCodeAsync(
            @RequestParam @NotBlank String description,
            @RequestParam("file") MultipartFile previousCode,
            @RequestParam("file") MultipartFile interfaceWord) throws Exception {

        // 0. 同步抽取文本（IO 轻量，串行即可）
        String frontCode  = textExtractor.extract(previousCode);
        String apiDoc     = textExtractor.extract(interfaceWord);

        // 1. 构造无依赖可并行任务
        CompletableFuture<AIProcessCode> sqlFuture = CompletableFuture.supplyAsync(() ->
                aiProcessCodeService.CreateSQLCode(
                        "根据前端页面代码和接口文档生成SQL代码。\n前端代码：" + frontCode + "\n接口文档：" + apiDoc), pool);

        CompletableFuture<AIProcessCode> voFuture  = CompletableFuture.supplyAsync(() ->
                aiProcessCodeService.CreateVOCode(
                        "根据接口文档生成VO类。\n接口文档：" + apiDoc), pool);

        // 2. 阻塞获取前置结果后继续提交依赖任务
        CompletableFuture<AIProcessCode> doFuture     = sqlFuture.thenApplyAsync(sqlResult ->
                aiProcessCodeService.CreateDOCode(
                        "根据SQL代码生成DO类。\nSQL代码：" + sqlResult.getSqlCode()), pool);

        CompletableFuture<AIProcessCode> mapperFuture = CompletableFuture.supplyAsync(() ->
        {
            try {
                return aiProcessCodeService.CreateMapperCode(
                        "根据DO类和VO类生成Mapper类。\nDO类：" + doFuture.get().getDoCode()
                                + "\nVO类：" + voFuture.get().getVoCode());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            }
        }, pool);

        CompletableFuture<AIProcessCode> serviceFuture = CompletableFuture.supplyAsync(() ->
        {
            try {
                return aiProcessCodeService.CreateServiceCode(
                        "根据VO类和Mapper类生成Service接口。\nVO类：" + voFuture.get().getVoCode()
                                + "\nMapper类：" + mapperFuture.get().getMapperCode());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            }
        }, pool);

        CompletableFuture<AIProcessCode> serviceImplFuture = serviceFuture.thenApplyAsync(serviceResult ->
                aiProcessCodeService.CreateServiceImplCode(
                        "根据Service接口生成ServiceImpl类。\nService接口：" + serviceResult.getServiceCode()), pool);

        CompletableFuture<AIProcessCode> controllerFuture = serviceImplFuture.thenApplyAsync(serviceImplResult ->
                aiProcessCodeService.CreateControllerCode(
                        "根据接口文档和ServiceImpl类生成Controller类。\n接口文档：" + apiDoc
                                + "\nServiceImpl类：" + serviceImplResult.getServiceImplCode()), pool);

        // 3. 汇总 & 并行落盘
        AIProcessCode result = new AIProcessCode();
        result.setDoCode(doFuture.get().getDoCode());
        result.setVoCode(voFuture.get().getVoCode());
        result.setMapperCode(mapperFuture.get().getMapperCode());
        result.setSqlCode(sqlFuture.get().getSqlCode());
        result.setServiceCode(serviceFuture.get().getServiceCode());
        result.setServiceImplCode(serviceImplFuture.get().getServiceImplCode());
        result.setControllerCode(controllerFuture.get().getControllerCode());
        CompletableFuture.runAsync(() -> saveAll(result), pool);
        return result;
    }



    /**
     * 获取项目列表（分页）
     */
    @GetMapping("/page")
    @Operation(summary = "获取项目列表")
    public ResponseEntity<PageResult<AppProjectsListRespVO>> getProjectsPage(AppProjectsPageReqVO pageReqVO) {
        PageResult<AppProjectsListRespVO> page = projectsService.getProjectsPageWithCreatorName(pageReqVO);
        return ResponseEntity.ok(page);
    }

    /**
     * 根据id修改项目状态
     */
    @PostMapping("/update-status")
    @Operation(summary = "修改项目状态")
    public ResponseEntity<Void> updateProjectStatus(@RequestBody Map<String, Long> request) {
        Long id = request.get("id");
        if (id == null) {
            throw new IllegalArgumentException("请求中缺少 id 参数");
        }
        projectsService.updateProjectStatus(id);
        return ResponseEntity.ok().build();
    }


    private void saveAll(AIProcessCode c) {
        try {
            sqlSaveTool.save(c);
            doSaveTool.save(c);
            voSaveTool.save(c);
            mapperSaveTool.save(c);
            serviceSaveTool.save(c);
            serviceImplSaveTool.save(c);
            controllerSaveTool.save(c);
        } catch (IOException e) {
            throw new RuntimeException("保存代码失败", e);
        }
    }


    /**
     * 处理项目代码生成请求的接口方法。
     * 该方法接收多个文档文件和描述性文字，通过异步任务生成项目代码，包括 DO、VO、Mapper、Service、ServiceImpl 和 Controller 类，以及前端 Vue 代码。
     * 生成的代码将被保存到指定路径。
     *
     * @param description 项目描述性文字，用于生成代码时的上下文信息。
     * @param frontEndCode 前端页面代码文件，用于生成前端 Vue 代码。
     * @param sqlDoc SQL 文档文件，用于生成 DO 类。
     * @param requirementDoc 需求文档文件，暂未直接用于代码生成，但可能用于上下文信息。
     * @param designDoc 设计文档文件，暂未直接用于代码生成，但可能用于上下文信息。
     * @param processDoc 流程文档文件，暂未直接用于代码生成，但可能用于上下文信息。
     * @param interfaceDoc 接口文档文件，用于生成 VO 类、Controller 类以及前端 Vue 代码。
     * @return AIProcessCode 包含生成的各类代码的实体类。
     * @throws ResponseStatusException 如果文件内容提取失败、代码生成失败或保存失败时抛出。
     */
    @PostMapping("/generate-project-code-mosaic")
    public AIProcessCode generateProjectCodeTest(
            @RequestParam @NotBlank(message = "描述性文字不能为空") String description,
            @RequestParam("frontEndCode") MultipartFile frontEndCode,
            @RequestParam("sqlDoc") MultipartFile sqlDoc,
            @RequestParam("requirementDoc") MultipartFile requirementDoc,
            @RequestParam("designDoc") MultipartFile designDoc,
            @RequestParam("processDoc") MultipartFile processDoc,
            @RequestParam("interfaceDoc") MultipartFile interfaceDoc) {

        // 1. 基础校验
        List<MultipartFile> files = Arrays.asList(frontEndCode, sqlDoc, requirementDoc, designDoc, processDoc, interfaceDoc);
        checkFileNames(files);
        List<String> allowedExtensions = List.of(".txt", ".doc", ".docx", ".pdf");
        checkFileExtensions(files, allowedExtensions);

        // 2. 解析文档内容
        Map<String, String> docContents = new ConcurrentHashMap<>();
        try {
            docContents = extractDocContents(files);
        } catch (IOException e) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "文件内容提取失败", e);
        }

        // 3. 创建实体类保存文档内容
        ProjectDocument projectDocument = new ProjectDocument();
        projectDocument.setFrontEndCode(docContents.get("frontEndCode"));
        projectDocument.setSqlDoc(docContents.get("sqlDoc"));
        projectDocument.setRequirementDoc(docContents.get("requirementDoc"));
        projectDocument.setDesignDoc(docContents.get("designDoc"));
        projectDocument.setProcessDoc(docContents.get("processDoc"));
        projectDocument.setInterfaceDoc(docContents.get("interfaceDoc"));

        // 4. 根据文档内容生成代码
        AIProcessCode aiProcessCode = new AIProcessCode();

        // 4.1 根据 SQL 文档直接生成 DO 类
        String DOcontent = "请根据 SQL 文档生成 DO 类。SQL 文档：" + docContents.get("sqlDoc");
        CompletableFuture<AIProcessCode> doFuture = CompletableFuture.supplyAsync(() -> {
            try {
                AIProcessCode doCode = aiProcessCodeService.CreateDOCode(DOcontent);
                aiProcessCode.setDoCode(doCode.getDoCode());
                return doCode;
            } catch (Exception e) {
                throw new CompletionException("生成 DO 类失败", e);
            }
        }, pool);

        // 4.2 根据接口文档生成 VO 类
        String VOcontent = "请根据接口文档生成 VO 类。接口文档：" + docContents.get("interfaceDoc");
        CompletableFuture<AIProcessCode> voFuture = CompletableFuture.supplyAsync(() -> {
            try {
                AIProcessCode voCode = aiProcessCodeService.CreateVOCode(VOcontent);
                aiProcessCode.setVoCode(voCode.getVoCode());
                return voCode;
            } catch (ConcurrentModificationException e) {
                // 捕获并发修改异常，并进行处理
                throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "生成 VO 类失败，发生并发修改异常", e);
            } catch (CompletionException e) {
                // 捕获其他异常，并进行处理
                throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "代码生成失败", e);
            }
        }, pool);

        // 4.3 根据 DO 类和 VO 类生成 Mapper 类
        CompletableFuture<AIProcessCode> mapperFuture = doFuture.thenCombineAsync(voFuture, (doCode, voCode) -> {
            try {
                String MAPPERcontent = "请根据 DO 类和 VO 类生成 Mapper 类。DO 类：" + doCode.getDoCode() + "VO 类：" + voCode.getVoCode();
                AIProcessCode mapperCode = aiProcessCodeService.CreateMapperCode(MAPPERcontent);
                aiProcessCode.setMapperCode(mapperCode.getMapperCode());
                return mapperCode;
            } catch (Exception e) {
                throw new CompletionException("生成 Mapper 类失败", e);
            }
        }, pool);

        // 4.4 根据 VO 类和 Mapper 类生成 Service 接口
        CompletableFuture<AIProcessCode> serviceFuture = voFuture.thenCombineAsync(mapperFuture, (voCode, mapperCode) -> {
            try {
                String SERVICEcontent = "请根据 VO 类和 Mapper 类生成 Service 接口。VO 类：" + voCode.getVoCode() + "Mapper 类：" + mapperCode.getMapperCode();
                AIProcessCode serviceCode = aiProcessCodeService.CreateServiceCode(SERVICEcontent);
                aiProcessCode.setServiceCode(serviceCode.getServiceCode());
                return serviceCode;
            } catch (Exception e) {
                throw new CompletionException("生成 Service 接口失败", e);
            }
        }, pool);

        // 4.5 根据 Service 接口生成 ServiceImpl 类
        CompletableFuture<AIProcessCode> serviceImplFuture = serviceFuture.thenApplyAsync(serviceCode -> {
            try {
                String SERVICEIMPLcontent = "请根据 Service 接口生成 ServiceImpl 类。Service 接口：" + serviceCode.getServiceCode();
                AIProcessCode serviceImplCode = aiProcessCodeService.CreateServiceImplCode(SERVICEIMPLcontent);
                aiProcessCode.setServiceImplCode(serviceImplCode.getServiceImplCode());
                return serviceImplCode;
            } catch (Exception e) {
                throw new CompletionException("生成 ServiceImpl 类失败", e);
            }
        }, pool);

        // 4.6 根据接口文档和 ServiceImpl 类生成 Controller 类
        Map<String, String> finalDocContents = new ConcurrentHashMap<>(docContents);
        CompletableFuture<AIProcessCode> controllerFuture = serviceImplFuture.thenApplyAsync(serviceImplCode -> {
            try {
                String CONTROLLERcontent = "请根据接口文档和 ServiceImpl 类生成 Controller 类。接口文档：" + finalDocContents.get("interfaceDoc") + "ServiceImpl 类：" + serviceImplCode.getServiceImplCode();
                AIProcessCode controllerCode = aiProcessCodeService.CreateControllerCode(CONTROLLERcontent);
                aiProcessCode.setControllerCode(controllerCode.getControllerCode());
                return controllerCode;
            } catch (Exception e) {
                throw new CompletionException("生成 Controller 类失败", e);
            }
        }, pool);

        // 4.7 根据前端页面代码和接口文档生成前端 Vue 代码
        Map<String, String> finalDocContents1 = new ConcurrentHashMap<>(docContents);
        CompletableFuture<AIProcessCode> vueFuture = CompletableFuture.supplyAsync(() -> {
            try {
                String VUEcontent = "请根据前端页面代码和接口文档生成前端 Vue 代码。前端页面代码：" + finalDocContents1.get("frontEndCode") + "接口文档：" + finalDocContents1.get("interfaceDoc");
                AIProcessCode vueCode = aiProcessCodeService.CreateVueCode(VUEcontent);
                aiProcessCode.setVueCode1(vueCode.getVueCode1());
                return vueCode;
            } catch (Exception e) {
                throw new CompletionException("生成 Vue 代码失败", e);
            }
        }, pool);

        // 5. 汇总 & 并行落盘
        CompletableFuture<Void> saveFuture = CompletableFuture.allOf(
                doFuture.thenAcceptAsync(doCode -> {
                    try {
                        doSaveTool.save(doCode);
                    } catch (IOException e) {
                        throw new CompletionException("保存 DO 类失败", e);
                    }
                }, pool),
                voFuture.thenAcceptAsync(voCode -> {
                    try {
                        voSaveTool.save(voCode);
                    } catch (IOException e) {
                        throw new CompletionException("保存 VO 类失败", e);
                    }
                }, pool),
                mapperFuture.thenAcceptAsync(mapperCode -> {
                    try {
                        mapperSaveTool.save(mapperCode);
                    } catch (IOException e) {
                        throw new CompletionException("保存 Mapper 类失败", e);
                    }
                }, pool),
                serviceFuture.thenAcceptAsync(serviceCode -> {
                    try {
                        serviceSaveTool.save(serviceCode);
                    } catch (IOException e) {
                        throw new CompletionException("保存 Service 接口失败", e);
                    }
                }, pool),
                serviceImplFuture.thenAcceptAsync(serviceImplCode -> {
                    try {
                        serviceImplSaveTool.save(serviceImplCode);
                    } catch (IOException e) {
                        throw new CompletionException("保存 ServiceImpl 类失败", e);
                    }
                }, pool),
                controllerFuture.thenAcceptAsync(controllerCode -> {
                    try {
                        controllerSaveTool.save(controllerCode);
                    } catch (IOException e) {
                        throw new CompletionException("保存 Controller 类失败", e);
                    }
                }, pool),
                vueFuture.thenAcceptAsync(vueCode -> {
                    try {
                        vueSaveTool.save(vueCode);
                    } catch (IOException e) {
                        throw new CompletionException("保存 Vue 代码失败", e);
                    }
                }, pool)
        );

        // 6. 等待所有任务完成并返回结果
        try {
            saveFuture.join();
        } catch (CompletionException e) {
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "代码生成失败", e);
        }

        return aiProcessCode;
    }

    /**
     * 校验文件名是否为空。
     * 如果文件名为空，则抛出 IllegalArgumentException 异常。
     *
     * @param files 文件列表
     */
    private void checkFileNames(List<MultipartFile> files) {
        for (MultipartFile file : files) {
            if (file.getOriginalFilename() == null) {
                throw new IllegalArgumentException("文件名不能为空");
            }
        }
    }

    /**
     * 校验文件扩展名是否符合要求。
     * 如果文件扩展名不在允许的扩展名列表中，则抛出 IllegalArgumentException 异常。
     *
     * @param files 文件列表
     * @param allowedExtensions 允许的文件扩展名列表
     */
    private void checkFileExtensions(List<MultipartFile> files, List<String> allowedExtensions) {
        for (MultipartFile file : files) {
            String ext = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf('.')).toLowerCase();
            if (!allowedExtensions.contains(ext)) {
                throw new IllegalArgumentException("本接口仅支持 " + allowedExtensions + " 文件");
            }
        }
    }

    /**
     * 提取文档内容。
     * 遍历文件列表，提取每个文件的内容，并将内容存储到 Map 中。
     *
     * @param files 文件列表
     * @return 包含文件内容的 Map
     * @throws IOException 如果文件内容提取失败时抛出
     */
    private Map<String, String> extractDocContents(List<MultipartFile> files) throws IOException {
        Map<String, String> docContents = new ConcurrentHashMap<>();
        for (MultipartFile file : files) {
            String content = textExtractor.extract(file); // 提取文件内容
            docContents.put(file.getName(), content); // 将内容存储到 Map 中
        }
        return docContents;
    }



    /**
     * AI分析报告功能（支持调用工具）
     *
     * @param message
     * @param chatId
     * @return
     */
//    public String doChatWithTools (String message, String chatId) {
//        McpTransport transport = new StdioMcpTransport.Builder()
//                .command(List.of("/usr/bin/npm", "exec", "@modelcontextprotocol/server-everything@0.6.2")) // 替换为实际的命令
//                .logEvents(true) // 如果希望在日志中看到事件
//                .build();
//
//        McpClient mcpClient = new DefaultMcpClient.Builder()
//                .transport(transport)
//                .build();
//        return mcpClient.chat(message, chatId);
//    }




}
